Merge branch 'depends/rmk/devel-stable' into next/cleanup
Arnd Bergmann [Sat, 8 Oct 2011 19:07:42 +0000 (21:07 +0200)]
405 files changed:
arch/arm/Kconfig
arch/arm/Kconfig.debug
arch/arm/boot/compressed/.gitignore
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/atags_to_fdt.c [new file with mode: 0644]
arch/arm/boot/compressed/head.S
arch/arm/boot/compressed/libfdt_env.h [new file with mode: 0644]
arch/arm/boot/compressed/misc.c
arch/arm/boot/compressed/string.c [new file with mode: 0644]
arch/arm/boot/compressed/vmlinux.lds.in
arch/arm/common/gic.c
arch/arm/include/asm/dma-mapping.h
arch/arm/include/asm/hardware/gic.h
arch/arm/include/asm/hw_breakpoint.h
arch/arm/include/asm/mach/arch.h
arch/arm/include/asm/mach/map.h
arch/arm/include/asm/memory.h
arch/arm/include/asm/pgtable.h
arch/arm/include/asm/pmu.h
arch/arm/include/asm/proc-fns.h
arch/arm/include/asm/suspend.h
arch/arm/kernel/Makefile
arch/arm/kernel/hw_breakpoint.c
arch/arm/kernel/kprobes-arm.c
arch/arm/kernel/kprobes-test-arm.c [new file with mode: 0644]
arch/arm/kernel/kprobes-test-thumb.c [new file with mode: 0644]
arch/arm/kernel/kprobes-test.c [new file with mode: 0644]
arch/arm/kernel/kprobes-test.h [new file with mode: 0644]
arch/arm/kernel/kprobes-thumb.c
arch/arm/kernel/kprobes.h
arch/arm/kernel/perf_event.c
arch/arm/kernel/perf_event_v6.c
arch/arm/kernel/perf_event_v7.c
arch/arm/kernel/perf_event_xscale.c
arch/arm/kernel/pmu.c
arch/arm/kernel/setup.c
arch/arm/kernel/sleep.S
arch/arm/kernel/suspend.c [new file with mode: 0644]
arch/arm/mach-at91/at91sam9g45.c
arch/arm/mach-at91/include/mach/at91sam9g45.h
arch/arm/mach-bcmring/include/mach/memory.h
arch/arm/mach-bcmring/mm.c
arch/arm/mach-clps711x/autcpu12.c
arch/arm/mach-clps711x/cdb89712.c
arch/arm/mach-clps711x/ceiva.c
arch/arm/mach-clps711x/clep7312.c
arch/arm/mach-clps711x/edb7211-arch.c
arch/arm/mach-clps711x/fortunet.c
arch/arm/mach-clps711x/p720t.c
arch/arm/mach-cns3xxx/cns3420vb.c
arch/arm/mach-davinci/board-da830-evm.c
arch/arm/mach-davinci/board-da850-evm.c
arch/arm/mach-davinci/board-dm355-evm.c
arch/arm/mach-davinci/board-dm355-leopard.c
arch/arm/mach-davinci/board-dm365-evm.c
arch/arm/mach-davinci/board-dm644x-evm.c
arch/arm/mach-davinci/board-dm646x-evm.c
arch/arm/mach-davinci/board-mityomapl138.c
arch/arm/mach-davinci/board-neuros-osd2.c
arch/arm/mach-davinci/board-omapl138-hawk.c
arch/arm/mach-davinci/board-sffsdr.c
arch/arm/mach-davinci/board-tnetv107x-evm.c
arch/arm/mach-davinci/common.c
arch/arm/mach-davinci/include/mach/memory.h
arch/arm/mach-dove/cm-a510.c
arch/arm/mach-dove/dove-db-setup.c
arch/arm/mach-ebsa110/core.c
arch/arm/mach-ep93xx/adssphere.c
arch/arm/mach-ep93xx/edb93xx.c
arch/arm/mach-ep93xx/gesbc9312.c
arch/arm/mach-ep93xx/micro9.c
arch/arm/mach-ep93xx/simone.c
arch/arm/mach-ep93xx/snappercl15.c
arch/arm/mach-ep93xx/ts72xx.c
arch/arm/mach-exynos4/mach-armlex4210.c
arch/arm/mach-exynos4/mach-nuri.c
arch/arm/mach-exynos4/mach-smdkv310.c
arch/arm/mach-exynos4/mach-universal_c210.c
arch/arm/mach-footbridge/cats-hw.c
arch/arm/mach-footbridge/ebsa285.c
arch/arm/mach-footbridge/netwinder-hw.c
arch/arm/mach-footbridge/personal.c
arch/arm/mach-gemini/board-nas4220b.c
arch/arm/mach-gemini/board-rut1xx.c
arch/arm/mach-gemini/board-wbd111.c
arch/arm/mach-gemini/board-wbd222.c
arch/arm/mach-h720x/h7201-eval.c
arch/arm/mach-h720x/h7202-eval.c
arch/arm/mach-imx/mach-armadillo5x0.c
arch/arm/mach-imx/mach-cpuimx27.c
arch/arm/mach-imx/mach-cpuimx35.c
arch/arm/mach-imx/mach-eukrea_cpuimx25.c
arch/arm/mach-imx/mach-imx27_visstrim_m10.c
arch/arm/mach-imx/mach-imx27ipcam.c
arch/arm/mach-imx/mach-imx27lite.c
arch/arm/mach-imx/mach-kzm_arm11_01.c
arch/arm/mach-imx/mach-mx1ads.c
arch/arm/mach-imx/mach-mx21ads.c
arch/arm/mach-imx/mach-mx25_3ds.c
arch/arm/mach-imx/mach-mx27_3ds.c
arch/arm/mach-imx/mach-mx27ads.c
arch/arm/mach-imx/mach-mx31_3ds.c
arch/arm/mach-imx/mach-mx31ads.c
arch/arm/mach-imx/mach-mx31lilly.c
arch/arm/mach-imx/mach-mx31lite.c
arch/arm/mach-imx/mach-mx31moboard.c
arch/arm/mach-imx/mach-mx35_3ds.c
arch/arm/mach-imx/mach-mxt_td60.c
arch/arm/mach-imx/mach-pca100.c
arch/arm/mach-imx/mach-pcm037.c
arch/arm/mach-imx/mach-pcm038.c
arch/arm/mach-imx/mach-pcm043.c
arch/arm/mach-imx/mach-qong.c
arch/arm/mach-imx/mach-scb9328.c
arch/arm/mach-integrator/integrator_ap.c
arch/arm/mach-integrator/integrator_cp.c
arch/arm/mach-iop13xx/iq81340mc.c
arch/arm/mach-iop13xx/iq81340sc.c
arch/arm/mach-iop32x/em7210.c
arch/arm/mach-iop32x/glantank.c
arch/arm/mach-iop32x/iq31244.c
arch/arm/mach-iop32x/iq80321.c
arch/arm/mach-iop32x/n2100.c
arch/arm/mach-iop33x/iq80331.c
arch/arm/mach-iop33x/iq80332.c
arch/arm/mach-ixp2000/enp2611.c
arch/arm/mach-ixp2000/ixdp2400.c
arch/arm/mach-ixp2000/ixdp2800.c
arch/arm/mach-ixp2000/ixdp2x01.c
arch/arm/mach-ixp23xx/espresso.c
arch/arm/mach-ixp23xx/ixdp2351.c
arch/arm/mach-ixp23xx/roadrunner.c
arch/arm/mach-ixp4xx/avila-setup.c
arch/arm/mach-ixp4xx/coyote-setup.c
arch/arm/mach-ixp4xx/dsmg600-setup.c
arch/arm/mach-ixp4xx/fsg-setup.c
arch/arm/mach-ixp4xx/gateway7001-setup.c
arch/arm/mach-ixp4xx/goramo_mlr.c
arch/arm/mach-ixp4xx/gtwx5715-setup.c
arch/arm/mach-ixp4xx/ixdp425-setup.c
arch/arm/mach-ixp4xx/nas100d-setup.c
arch/arm/mach-ixp4xx/nslu2-setup.c
arch/arm/mach-ixp4xx/vulcan-setup.c
arch/arm/mach-ixp4xx/wg302v2-setup.c
arch/arm/mach-kirkwood/d2net_v2-setup.c
arch/arm/mach-kirkwood/db88f6281-bp-setup.c
arch/arm/mach-kirkwood/dockstar-setup.c
arch/arm/mach-kirkwood/guruplug-setup.c
arch/arm/mach-kirkwood/mv88f6281gtw_ge-setup.c
arch/arm/mach-kirkwood/netspace_v2-setup.c
arch/arm/mach-kirkwood/netxbig_v2-setup.c
arch/arm/mach-kirkwood/openrd-setup.c
arch/arm/mach-kirkwood/rd88f6192-nas-setup.c
arch/arm/mach-kirkwood/rd88f6281-setup.c
arch/arm/mach-kirkwood/sheevaplug-setup.c
arch/arm/mach-kirkwood/t5325-setup.c
arch/arm/mach-kirkwood/ts219-setup.c
arch/arm/mach-kirkwood/ts41x-setup.c
arch/arm/mach-ks8695/board-acs5k.c
arch/arm/mach-ks8695/board-dsm320.c
arch/arm/mach-ks8695/board-micrel.c
arch/arm/mach-lpc32xx/phy3250.c
arch/arm/mach-msm/board-halibut.c
arch/arm/mach-msm/board-mahimahi.c
arch/arm/mach-msm/board-msm7x27.c
arch/arm/mach-msm/board-msm7x30.c
arch/arm/mach-msm/board-qsd8x50.c
arch/arm/mach-msm/board-sapphire.c
arch/arm/mach-msm/board-trout.c
arch/arm/mach-mv78xx0/buffalo-wxl-setup.c
arch/arm/mach-mv78xx0/db78x00-bp-setup.c
arch/arm/mach-mv78xx0/rd78x00-masa-setup.c
arch/arm/mach-mx5/board-cpuimx51.c
arch/arm/mach-mx5/board-cpuimx51sd.c
arch/arm/mach-mx5/board-mx51_3ds.c
arch/arm/mach-mx5/board-mx51_babbage.c
arch/arm/mach-mx5/board-mx51_efikamx.c
arch/arm/mach-mx5/board-mx51_efikasb.c
arch/arm/mach-netx/nxdb500.c
arch/arm/mach-netx/nxdkn.c
arch/arm/mach-netx/nxeb500hmi.c
arch/arm/mach-nomadik/board-nhk8815.c
arch/arm/mach-nuc93x/mach-nuc932evb.c
arch/arm/mach-omap1/board-ams-delta.c
arch/arm/mach-omap1/board-fsample.c
arch/arm/mach-omap1/board-generic.c
arch/arm/mach-omap1/board-h2.c
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap1/board-htcherald.c
arch/arm/mach-omap1/board-innovator.c
arch/arm/mach-omap1/board-nokia770.c
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-omap1/board-palmte.c
arch/arm/mach-omap1/board-palmtt.c
arch/arm/mach-omap1/board-palmz71.c
arch/arm/mach-omap1/board-perseus2.c
arch/arm/mach-omap1/board-sx1.c
arch/arm/mach-omap1/board-voiceblue.c
arch/arm/mach-omap1/io.c
arch/arm/mach-omap2/board-2430sdp.c
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/board-3630sdp.c
arch/arm/mach-omap2/board-4430sdp.c
arch/arm/mach-omap2/board-am3517crane.c
arch/arm/mach-omap2/board-am3517evm.c
arch/arm/mach-omap2/board-apollon.c
arch/arm/mach-omap2/board-cm-t35.c
arch/arm/mach-omap2/board-cm-t3517.c
arch/arm/mach-omap2/board-devkit8000.c
arch/arm/mach-omap2/board-generic.c
arch/arm/mach-omap2/board-h4.c
arch/arm/mach-omap2/board-igep0020.c
arch/arm/mach-omap2/board-ldp.c
arch/arm/mach-omap2/board-n8x0.c
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3evm.c
arch/arm/mach-omap2/board-omap3logic.c
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-omap3stalker.c
arch/arm/mach-omap2/board-omap3touchbook.c
arch/arm/mach-omap2/board-omap4panda.c
arch/arm/mach-omap2/board-overo.c
arch/arm/mach-omap2/board-rm680.c
arch/arm/mach-omap2/board-rx51.c
arch/arm/mach-omap2/board-ti8168evm.c
arch/arm/mach-omap2/board-zoom.c
arch/arm/mach-omap2/io.c
arch/arm/mach-orion5x/d2net-setup.c
arch/arm/mach-orion5x/db88f5281-setup.c
arch/arm/mach-orion5x/dns323-setup.c
arch/arm/mach-orion5x/edmini_v2-setup.c
arch/arm/mach-orion5x/kurobox_pro-setup.c
arch/arm/mach-orion5x/ls-chl-setup.c
arch/arm/mach-orion5x/ls_hgl-setup.c
arch/arm/mach-orion5x/lsmini-setup.c
arch/arm/mach-orion5x/mss2-setup.c
arch/arm/mach-orion5x/mv2120-setup.c
arch/arm/mach-orion5x/net2big-setup.c
arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
arch/arm/mach-orion5x/rd88f5182-setup.c
arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c
arch/arm/mach-orion5x/terastation_pro2-setup.c
arch/arm/mach-orion5x/ts209-setup.c
arch/arm/mach-orion5x/ts409-setup.c
arch/arm/mach-orion5x/ts78xx-setup.c
arch/arm/mach-orion5x/wnr854t-setup.c
arch/arm/mach-orion5x/wrt350n-v2-setup.c
arch/arm/mach-pnx4008/core.c
arch/arm/mach-prima2/prima2.c
arch/arm/mach-pxa/balloon3.c
arch/arm/mach-pxa/capc7117.c
arch/arm/mach-pxa/cm-x2xx.c
arch/arm/mach-pxa/cm-x300.c
arch/arm/mach-pxa/colibri-pxa270.c
arch/arm/mach-pxa/colibri-pxa300.c
arch/arm/mach-pxa/colibri-pxa320.c
arch/arm/mach-pxa/csb726.c
arch/arm/mach-pxa/em-x270.c
arch/arm/mach-pxa/eseries.c
arch/arm/mach-pxa/ezx.c
arch/arm/mach-pxa/gumstix.c
arch/arm/mach-pxa/h5000.c
arch/arm/mach-pxa/himalaya.c
arch/arm/mach-pxa/hx4700.c
arch/arm/mach-pxa/icontrol.c
arch/arm/mach-pxa/littleton.c
arch/arm/mach-pxa/lpd270.c
arch/arm/mach-pxa/magician.c
arch/arm/mach-pxa/mainstone.c
arch/arm/mach-pxa/mioa701.c
arch/arm/mach-pxa/mp900.c
arch/arm/mach-pxa/palmld.c
arch/arm/mach-pxa/palmt5.c
arch/arm/mach-pxa/palmtc.c
arch/arm/mach-pxa/palmte2.c
arch/arm/mach-pxa/palmtreo.c
arch/arm/mach-pxa/palmtx.c
arch/arm/mach-pxa/palmz72.c
arch/arm/mach-pxa/pcm027.c
arch/arm/mach-pxa/raumfeld.c
arch/arm/mach-pxa/saar.c
arch/arm/mach-pxa/saarb.c
arch/arm/mach-pxa/stargate2.c
arch/arm/mach-pxa/tavorevb.c
arch/arm/mach-pxa/tavorevb3.c
arch/arm/mach-pxa/trizeps4.c
arch/arm/mach-pxa/viper.c
arch/arm/mach-pxa/vpac270.c
arch/arm/mach-pxa/xcep.c
arch/arm/mach-pxa/z2.c
arch/arm/mach-pxa/zeus.c
arch/arm/mach-pxa/zylonite.c
arch/arm/mach-realview/realview_eb.c
arch/arm/mach-realview/realview_pb1176.c
arch/arm/mach-realview/realview_pb11mp.c
arch/arm/mach-realview/realview_pba8.c
arch/arm/mach-realview/realview_pbx.c
arch/arm/mach-rpc/riscpc.c
arch/arm/mach-s3c2410/mach-amlm5900.c
arch/arm/mach-s3c2410/mach-bast.c
arch/arm/mach-s3c2410/mach-h1940.c
arch/arm/mach-s3c2410/mach-n30.c
arch/arm/mach-s3c2410/mach-otom.c
arch/arm/mach-s3c2410/mach-qt2410.c
arch/arm/mach-s3c2410/mach-smdk2410.c
arch/arm/mach-s3c2410/mach-tct_hammer.c
arch/arm/mach-s3c2410/mach-vr1000.c
arch/arm/mach-s3c2412/mach-jive.c
arch/arm/mach-s3c2412/mach-smdk2413.c
arch/arm/mach-s3c2412/mach-vstms.c
arch/arm/mach-s3c2416/mach-smdk2416.c
arch/arm/mach-s3c2440/mach-anubis.c
arch/arm/mach-s3c2440/mach-at2440evb.c
arch/arm/mach-s3c2440/mach-gta02.c
arch/arm/mach-s3c2440/mach-mini2440.c
arch/arm/mach-s3c2440/mach-nexcoder.c
arch/arm/mach-s3c2440/mach-osiris.c
arch/arm/mach-s3c2440/mach-rx1950.c
arch/arm/mach-s3c2440/mach-rx3715.c
arch/arm/mach-s3c2440/mach-smdk2440.c
arch/arm/mach-s3c2443/mach-smdk2443.c
arch/arm/mach-s3c64xx/cpu.c
arch/arm/mach-s3c64xx/include/mach/memory.h
arch/arm/mach-s3c64xx/mach-anw6410.c
arch/arm/mach-s3c64xx/mach-crag6410.c
arch/arm/mach-s3c64xx/mach-hmt.c
arch/arm/mach-s3c64xx/mach-mini6410.c
arch/arm/mach-s3c64xx/mach-ncp.c
arch/arm/mach-s3c64xx/mach-real6410.c
arch/arm/mach-s3c64xx/mach-smartq5.c
arch/arm/mach-s3c64xx/mach-smartq7.c
arch/arm/mach-s3c64xx/mach-smdk6400.c
arch/arm/mach-s3c64xx/mach-smdk6410.c
arch/arm/mach-s5p64x0/cpu.c
arch/arm/mach-s5p64x0/include/mach/memory.h
arch/arm/mach-s5p64x0/mach-smdk6440.c
arch/arm/mach-s5p64x0/mach-smdk6450.c
arch/arm/mach-s5pc100/mach-smdkc100.c
arch/arm/mach-s5pv210/cpu.c
arch/arm/mach-s5pv210/include/mach/memory.h
arch/arm/mach-s5pv210/mach-aquila.c
arch/arm/mach-s5pv210/mach-goni.c
arch/arm/mach-s5pv210/mach-smdkc110.c
arch/arm/mach-s5pv210/mach-smdkv210.c
arch/arm/mach-s5pv210/mach-torbreck.c
arch/arm/mach-sa1100/assabet.c
arch/arm/mach-sa1100/badge4.c
arch/arm/mach-sa1100/h3100.c
arch/arm/mach-sa1100/h3600.c
arch/arm/mach-sa1100/hackkit.c
arch/arm/mach-sa1100/jornada720.c
arch/arm/mach-sa1100/lart.c
arch/arm/mach-sa1100/nanoengine.c
arch/arm/mach-sa1100/shannon.c
arch/arm/mach-sa1100/simpad.c
arch/arm/mach-shark/core.c
arch/arm/mach-shmobile/board-ag5evm.c
arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-g3evm.c
arch/arm/mach-shmobile/board-g4evm.c
arch/arm/mach-shmobile/board-mackerel.c
arch/arm/mach-shmobile/include/mach/memory.h
arch/arm/mach-spear3xx/spear300_evb.c
arch/arm/mach-spear3xx/spear310_evb.c
arch/arm/mach-spear3xx/spear320_evb.c
arch/arm/mach-spear6xx/spear600_evb.c
arch/arm/mach-tcc8k/board-tcc8000-sdk.c
arch/arm/mach-tegra/board-harmony.c
arch/arm/mach-tegra/board-paz00.c
arch/arm/mach-tegra/board-seaboard.c
arch/arm/mach-tegra/board-trimslice.c
arch/arm/mach-u300/core.c
arch/arm/mach-u300/include/mach/memory.h
arch/arm/mach-u300/u300.c
arch/arm/mach-ux500/board-mop500.c
arch/arm/mach-ux500/board-u5500.c
arch/arm/mach-versatile/versatile_ab.c
arch/arm/mach-versatile/versatile_pb.c
arch/arm/mach-vexpress/v2m.c
arch/arm/mach-vt8500/bv07.c
arch/arm/mach-vt8500/wm8505_7in.c
arch/arm/mach-w90x900/mach-nuc910evb.c
arch/arm/mach-w90x900/mach-nuc950evb.c
arch/arm/mach-w90x900/mach-nuc960evb.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/init.c
arch/arm/mm/mmu.c
arch/arm/mm/proc-arm920.S
arch/arm/mm/proc-arm926.S
arch/arm/mm/proc-sa1100.S
arch/arm/mm/proc-v6.S
arch/arm/mm/proc-v7.S
arch/arm/mm/proc-xsc3.S
arch/arm/mm/proc-xscale.S
arch/arm/plat-mxc/include/mach/memory.h
arch/arm/plat-omap/include/plat/io.h
arch/arm/plat-omap/include/plat/memory.h
arch/arm/plat-omap/io.c
arch/arm/vfp/vfpmodule.c
include/linux/cpu_pm.h [new file with mode: 0644]
kernel/Makefile
kernel/cpu_pm.c [new file with mode: 0644]
kernel/events/core.c
kernel/power/Kconfig

index 9f1ff5c..2ad79b2 100644 (file)
@@ -29,6 +29,7 @@ config ARM
        select HAVE_GENERIC_HARDIRQS
        select HAVE_SPARSE_IRQ
        select GENERIC_IRQ_SHOW
+       select CPU_PM if (SUSPEND || CPU_IDLE)
        help
          The ARM series is a line of low-power-consumption RISC chip designs
          licensed by ARM Ltd and targeted at embedded applications and
@@ -1794,6 +1795,38 @@ config ZBOOT_ROM_SH_MOBILE_SDHI
 
 endchoice
 
+config ARM_APPENDED_DTB
+       bool "Use appended device tree blob to zImage (EXPERIMENTAL)"
+       depends on OF && !ZBOOT_ROM && EXPERIMENTAL
+       help
+         With this option, the boot code will look for a device tree binary
+         (DTB) appended to zImage
+         (e.g. cat zImage <filename>.dtb > zImage_w_dtb).
+
+         This is meant as a backward compatibility convenience for those
+         systems with a bootloader that can't be upgraded to accommodate
+         the documented boot protocol using a device tree.
+
+         Beware that there is very little in terms of protection against
+         this option being confused by leftover garbage in memory that might
+         look like a DTB header after a reboot if no actual DTB is appended
+         to zImage.  Do not leave this option active in a production kernel
+         if you don't intend to always append a DTB.  Proper passing of the
+         location into r2 of a bootloader provided DTB is always preferable
+         to this option.
+
+config ARM_ATAG_DTB_COMPAT
+       bool "Supplement the appended DTB with traditional ATAG information"
+       depends on ARM_APPENDED_DTB
+       help
+         Some old bootloaders can't be updated to a DTB capable one, yet
+         they provide ATAGs with memory configuration, the ramdisk address,
+         the kernel cmdline string, etc.  Such information is dynamically
+         provided by the bootloader and can't always be stored in a static
+         DTB.  To allow a device tree enabled kernel to be used with such
+         bootloaders, this option allows zImage to extract the information
+         from the ATAG list and store it at run time into the appended DTB.
+
 config CMDLINE
        string "Default kernel command string"
        default ""
index 81cbe40..be3a0f7 100644 (file)
@@ -129,4 +129,10 @@ config DEBUG_S3C_UART
          The uncompressor code port configuration is now handled
          by CONFIG_S3C_LOWLEVEL_UART_PORT.
 
+config ARM_KPROBES_TEST
+       tristate "Kprobes test module"
+       depends on KPROBES && MODULES
+       help
+         Perform tests of kprobes API and instruction set simulation.
+
 endmenu
index c602896..e0936a1 100644 (file)
@@ -5,3 +5,12 @@ piggy.lzo
 piggy.lzma
 vmlinux
 vmlinux.lds
+
+# borrowed libfdt files
+fdt.c
+fdt.h
+fdt_ro.c
+fdt_rw.c
+fdt_wip.c
+libfdt.h
+libfdt_internal.h
index 0c74a6f..e4f32a8 100644 (file)
@@ -26,6 +26,10 @@ HEAD = head.o
 OBJS   += misc.o decompress.o
 FONTC  = $(srctree)/drivers/video/console/font_acorn_8x8.c
 
+# string library code (-Os is enforced to keep it much smaller)
+OBJS           += string.o
+CFLAGS_string.o        := -Os
+
 #
 # Architecture dependencies
 #
@@ -89,21 +93,41 @@ suffix_$(CONFIG_KERNEL_GZIP) = gzip
 suffix_$(CONFIG_KERNEL_LZO)  = lzo
 suffix_$(CONFIG_KERNEL_LZMA) = lzma
 
+# Borrowed libfdt files for the ATAG compatibility mode
+
+libfdt         := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c
+libfdt_hdrs    := fdt.h libfdt.h libfdt_internal.h
+
+libfdt_objs    := $(addsuffix .o, $(basename $(libfdt)))
+
+$(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/libfdt/%
+       $(call cmd,shipped)
+
+$(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
+       $(addprefix $(obj)/,$(libfdt_hdrs))
+
+ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
+OBJS   += $(libfdt_objs) atags_to_fdt.o
+endif
+
 targets       := vmlinux vmlinux.lds \
                 piggy.$(suffix_y) piggy.$(suffix_y).o \
-                font.o font.c head.o misc.o $(OBJS)
+                lib1funcs.o lib1funcs.S font.o font.c head.o misc.o $(OBJS)
 
 # Make sure files are removed during clean
-extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S
+extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S $(libfdt) $(libfdt_hdrs)
 
 ifeq ($(CONFIG_FUNCTION_TRACER),y)
 ORIG_CFLAGS := $(KBUILD_CFLAGS)
 KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
 endif
 
-ccflags-y := -fpic -fno-builtin
+ccflags-y := -fpic -fno-builtin -I$(obj)
 asflags-y := -Wa,-march=all
 
+# Supply kernel BSS size to the decompressor via a linker symbol.
+KBSS_SZ = $(shell size $(obj)/../../../../vmlinux | awk 'END{print $$3}')
+LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ)
 # Supply ZRELADDR to the decompressor via a linker symbol.
 ifneq ($(CONFIG_AUTO_ZRELADDR),y)
 LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR)
@@ -123,7 +147,7 @@ LDFLAGS_vmlinux += -T
 # For __aeabi_uidivmod
 lib1funcs = $(obj)/lib1funcs.o
 
-$(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S FORCE
+$(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S
        $(call cmd,shipped)
 
 # We need to prevent any GOTOFF relocs being used with references
diff --git a/arch/arm/boot/compressed/atags_to_fdt.c b/arch/arm/boot/compressed/atags_to_fdt.c
new file mode 100644 (file)
index 0000000..6ce11c4
--- /dev/null
@@ -0,0 +1,97 @@
+#include <asm/setup.h>
+#include <libfdt.h>
+
+static int node_offset(void *fdt, const char *node_path)
+{
+       int offset = fdt_path_offset(fdt, node_path);
+       if (offset == -FDT_ERR_NOTFOUND)
+               offset = fdt_add_subnode(fdt, 0, node_path);
+       return offset;
+}
+
+static int setprop(void *fdt, const char *node_path, const char *property,
+                  uint32_t *val_array, int size)
+{
+       int offset = node_offset(fdt, node_path);
+       if (offset < 0)
+               return offset;
+       return fdt_setprop(fdt, offset, property, val_array, size);
+}
+
+static int setprop_string(void *fdt, const char *node_path,
+                         const char *property, const char *string)
+{
+       int offset = node_offset(fdt, node_path);
+       if (offset < 0)
+               return offset;
+       return fdt_setprop_string(fdt, offset, property, string);
+}
+
+static int setprop_cell(void *fdt, const char *node_path,
+                       const char *property, uint32_t val)
+{
+       int offset = node_offset(fdt, node_path);
+       if (offset < 0)
+               return offset;
+       return fdt_setprop_cell(fdt, offset, property, val);
+}
+
+/*
+ * Convert and fold provided ATAGs into the provided FDT.
+ *
+ * REturn values:
+ *    = 0 -> pretend success
+ *    = 1 -> bad ATAG (may retry with another possible ATAG pointer)
+ *    < 0 -> error from libfdt
+ */
+int atags_to_fdt(void *atag_list, void *fdt, int total_space)
+{
+       struct tag *atag = atag_list;
+       uint32_t mem_reg_property[2 * NR_BANKS];
+       int memcount = 0;
+       int ret;
+
+       /* make sure we've got an aligned pointer */
+       if ((u32)atag_list & 0x3)
+               return 1;
+
+       /* if we get a DTB here we're done already */
+       if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC))
+              return 0;
+
+       /* validate the ATAG */
+       if (atag->hdr.tag != ATAG_CORE ||
+           (atag->hdr.size != tag_size(tag_core) &&
+            atag->hdr.size != 2))
+               return 1;
+
+       /* let's give it all the room it could need */
+       ret = fdt_open_into(fdt, fdt, total_space);
+       if (ret < 0)
+               return ret;
+
+       for_each_tag(atag, atag_list) {
+               if (atag->hdr.tag == ATAG_CMDLINE) {
+                       setprop_string(fdt, "/chosen", "bootargs",
+                                       atag->u.cmdline.cmdline);
+               } else if (atag->hdr.tag == ATAG_MEM) {
+                       if (memcount >= sizeof(mem_reg_property)/4)
+                               continue;
+                       mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.start);
+                       mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.size);
+               } else if (atag->hdr.tag == ATAG_INITRD2) {
+                       uint32_t initrd_start, initrd_size;
+                       initrd_start = atag->u.initrd.start;
+                       initrd_size = atag->u.initrd.size;
+                       setprop_cell(fdt, "/chosen", "linux,initrd-start",
+                                       initrd_start);
+                       setprop_cell(fdt, "/chosen", "linux,initrd-end",
+                                       initrd_start + initrd_size);
+               }
+       }
+
+       if (memcount)
+               setprop(fdt, "/memory", "reg", mem_reg_property, 4*memcount);
+
+       return fdt_pack(fdt);
+}
index e95a598..9f5ac11 100644 (file)
@@ -216,6 +216,103 @@ restart:  adr     r0, LC0
                mov     r10, r6
 #endif
 
+               mov     r5, #0                  @ init dtb size to 0
+#ifdef CONFIG_ARM_APPENDED_DTB
+/*
+ *   r0  = delta
+ *   r2  = BSS start
+ *   r3  = BSS end
+ *   r4  = final kernel address
+ *   r5  = appended dtb size (still unknown)
+ *   r6  = _edata
+ *   r7  = architecture ID
+ *   r8  = atags/device tree pointer
+ *   r9  = size of decompressed image
+ *   r10 = end of this image, including  bss/stack/malloc space if non XIP
+ *   r11 = GOT start
+ *   r12 = GOT end
+ *   sp  = stack pointer
+ *
+ * if there are device trees (dtb) appended to zImage, advance r10 so that the
+ * dtb data will get relocated along with the kernel if necessary.
+ */
+
+               ldr     lr, [r6, #0]
+#ifndef __ARMEB__
+               ldr     r1, =0xedfe0dd0         @ sig is 0xd00dfeed big endian
+#else
+               ldr     r1, =0xd00dfeed
+#endif
+               cmp     lr, r1
+               bne     dtb_check_done          @ not found
+
+#ifdef CONFIG_ARM_ATAG_DTB_COMPAT
+               /*
+                * OK... Let's do some funky business here.
+                * If we do have a DTB appended to zImage, and we do have
+                * an ATAG list around, we want the later to be translated
+                * and folded into the former here.  To be on the safe side,
+                * let's temporarily move  the stack away into the malloc
+                * area.  No GOT fixup has occurred yet, but none of the
+                * code we're about to call uses any global variable.
+               */
+               add     sp, sp, #0x10000
+               stmfd   sp!, {r0-r3, ip, lr}
+               mov     r0, r8
+               mov     r1, r6
+               sub     r2, sp, r6
+               bl      atags_to_fdt
+
+               /*
+                * If returned value is 1, there is no ATAG at the location
+                * pointed by r8.  Try the typical 0x100 offset from start
+                * of RAM and hope for the best.
+                */
+               cmp     r0, #1
+               sub     r0, r4, #(TEXT_OFFSET - 0x100)
+               mov     r1, r6
+               sub     r2, sp, r6
+               blne    atags_to_fdt
+
+               ldmfd   sp!, {r0-r3, ip, lr}
+               sub     sp, sp, #0x10000
+#endif
+
+               mov     r8, r6                  @ use the appended device tree
+
+               /*
+                * Make sure that the DTB doesn't end up in the final
+                * kernel's .bss area. To do so, we adjust the decompressed
+                * kernel size to compensate if that .bss size is larger
+                * than the relocated code.
+                */
+               ldr     r5, =_kernel_bss_size
+               adr     r1, wont_overwrite
+               sub     r1, r6, r1
+               subs    r1, r5, r1
+               addhi   r9, r9, r1
+
+               /* Get the dtb's size */
+               ldr     r5, [r6, #4]
+#ifndef __ARMEB__
+               /* convert r5 (dtb size) to little endian */
+               eor     r1, r5, r5, ror #16
+               bic     r1, r1, #0x00ff0000
+               mov     r5, r5, ror #8
+               eor     r5, r5, r1, lsr #8
+#endif
+
+               /* preserve 64-bit alignment */
+               add     r5, r5, #7
+               bic     r5, r5, #7
+
+               /* relocate some pointers past the appended dtb */
+               add     r6, r6, r5
+               add     r10, r10, r5
+               add     sp, sp, r5
+dtb_check_done:
+#endif
+
 /*
  * Check to see if we will overwrite ourselves.
  *   r4  = final kernel address
@@ -223,15 +320,14 @@ restart:  adr     r0, LC0
  *   r10 = end of this image, including  bss/stack/malloc space if non XIP
  * We basically want:
  *   r4 - 16k page directory >= r10 -> OK
- *   r4 + image length <= current position (pc) -> OK
+ *   r4 + image length <= address of wont_overwrite -> OK
  */
                add     r10, r10, #16384
                cmp     r4, r10
                bhs     wont_overwrite
                add     r10, r4, r9
-   ARM(                cmp     r10, pc         )
- THUMB(                mov     lr, pc          )
- THUMB(                cmp     r10, lr         )
+               adr     r9, wont_overwrite
+               cmp     r10, r9
                bls     wont_overwrite
 
 /*
@@ -285,14 +381,16 @@ wont_overwrite:
  *   r2  = BSS start
  *   r3  = BSS end
  *   r4  = kernel execution address
+ *   r5  = appended dtb size (0 if not present)
  *   r7  = architecture ID
  *   r8  = atags pointer
  *   r11 = GOT start
  *   r12 = GOT end
  *   sp  = stack pointer
  */
-               teq     r0, #0
+               orrs    r1, r0, r5
                beq     not_relocated
+
                add     r11, r11, r0
                add     r12, r12, r0
 
@@ -307,12 +405,21 @@ wont_overwrite:
 
                /*
                 * Relocate all entries in the GOT table.
+                * Bump bss entries to _edata + dtb size
                 */
 1:             ldr     r1, [r11, #0]           @ relocate entries in the GOT
-               add     r1, r1, r0              @ table.  This fixes up the
-               str     r1, [r11], #4           @ C references.
+               add     r1, r1, r0              @ This fixes up C references
+               cmp     r1, r2                  @ if entry >= bss_start &&
+               cmphs   r3, r1                  @       bss_end > entry
+               addhi   r1, r1, r5              @    entry += dtb size
+               str     r1, [r11], #4           @ next entry
                cmp     r11, r12
                blo     1b
+
+               /* bump our bss pointers too */
+               add     r2, r2, r5
+               add     r3, r3, r5
+
 #else
 
                /*
diff --git a/arch/arm/boot/compressed/libfdt_env.h b/arch/arm/boot/compressed/libfdt_env.h
new file mode 100644 (file)
index 0000000..1f4e718
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _ARM_LIBFDT_ENV_H
+#define _ARM_LIBFDT_ENV_H
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <asm/byteorder.h>
+
+#define fdt16_to_cpu(x)                be16_to_cpu(x)
+#define cpu_to_fdt16(x)                cpu_to_be16(x)
+#define fdt32_to_cpu(x)                be32_to_cpu(x)
+#define cpu_to_fdt32(x)                cpu_to_be32(x)
+#define fdt64_to_cpu(x)                be64_to_cpu(x)
+#define cpu_to_fdt64(x)                cpu_to_be64(x)
+
+#endif
index 832d372..8e2a8fc 100644 (file)
 
 unsigned int __machine_arch_type;
 
-#define _LINUX_STRING_H_
-
 #include <linux/compiler.h>    /* for inline */
-#include <linux/types.h>       /* for size_t */
-#include <linux/stddef.h>      /* for NULL */
+#include <linux/types.h>
 #include <linux/linkage.h>
-#include <asm/string.h>
-
 
 static void putstr(const char *ptr);
 extern void error(char *x);
@@ -101,41 +96,6 @@ static void putstr(const char *ptr)
        flush();
 }
 
-
-void *memcpy(void *__dest, __const void *__src, size_t __n)
-{
-       int i = 0;
-       unsigned char *d = (unsigned char *)__dest, *s = (unsigned char *)__src;
-
-       for (i = __n >> 3; i > 0; i--) {
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-       }
-
-       if (__n & 1 << 2) {
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-               *d++ = *s++;
-       }
-
-       if (__n & 1 << 1) {
-               *d++ = *s++;
-               *d++ = *s++;
-       }
-
-       if (__n & 1)
-               *d++ = *s++;
-
-       return __dest;
-}
-
 /*
  * gzip declarations
  */
diff --git a/arch/arm/boot/compressed/string.c b/arch/arm/boot/compressed/string.c
new file mode 100644 (file)
index 0000000..36e53ef
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * arch/arm/boot/compressed/string.c
+ *
+ * Small subset of simple string routines
+ */
+
+#include <linux/string.h>
+
+void *memcpy(void *__dest, __const void *__src, size_t __n)
+{
+       int i = 0;
+       unsigned char *d = (unsigned char *)__dest, *s = (unsigned char *)__src;
+
+       for (i = __n >> 3; i > 0; i--) {
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+       }
+
+       if (__n & 1 << 2) {
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+               *d++ = *s++;
+       }
+
+       if (__n & 1 << 1) {
+               *d++ = *s++;
+               *d++ = *s++;
+       }
+
+       if (__n & 1)
+               *d++ = *s++;
+
+       return __dest;
+}
+
+void *memmove(void *__dest, __const void *__src, size_t count)
+{
+       unsigned char *d = __dest;
+       const unsigned char *s = __src;
+
+       if (__dest == __src)
+               return __dest;
+
+       if (__dest < __src)
+               return memcpy(__dest, __src, count);
+
+       while (count--)
+               d[count] = s[count];
+       return __dest;
+}
+
+size_t strlen(const char *s)
+{
+       const char *sc = s;
+
+       while (*sc != '\0')
+               sc++;
+       return sc - s;
+}
+
+int memcmp(const void *cs, const void *ct, size_t count)
+{
+       const unsigned char *su1 = cs, *su2 = ct, *end = su1 + count;
+       int res = 0;
+
+       while (su1 < end) {
+               res = *su1++ - *su2++;
+               if (res)
+                       break;
+       }
+       return res;
+}
+
+int strcmp(const char *cs, const char *ct)
+{
+       unsigned char c1, c2;
+       int res = 0;
+
+       do {
+               c1 = *cs++;
+               c2 = *ct++;
+               res = c1 - c2;
+               if (res)
+                       break;
+       } while (c1);
+       return res;
+}
+
+void *memchr(const void *s, int c, size_t count)
+{
+       const unsigned char *p = s;
+
+       while (count--)
+               if ((unsigned char)c == *p++)
+                       return (void *)(p - 1);
+       return NULL;
+}
+
+char *strchr(const char *s, int c)
+{
+       while (*s != (char)c)
+               if (*s++ == '\0')
+                       return NULL;
+       return (char *)s;
+}
+
+#undef memset
+
+void *memset(void *s, int c, size_t count)
+{
+       char *xs = s;
+       while (count--)
+               *xs++ = c;
+       return s;
+}
+
+void __memzero(void *s, size_t count)
+{
+       memset(s, 0, count);
+}
index 4e72883..4919f2a 100644 (file)
@@ -51,6 +51,10 @@ SECTIONS
   _got_start = .;
   .got                 : { *(.got) }
   _got_end = .;
+
+  /* ensure the zImage file size is always a multiple of 64 bits */
+  /* (without a dummy byte, ld just ignores the empty section) */
+  .pad                 : { BYTE(0); . = ALIGN(8); }
   _edata = .;
 
   . = BSS_START;
index 3227ca9..734db99 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/smp.h>
+#include <linux/cpu_pm.h>
 #include <linux/cpumask.h>
 #include <linux/io.h>
 
@@ -276,6 +277,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
        if (gic_irqs > 1020)
                gic_irqs = 1020;
 
+       gic->gic_irqs = gic_irqs;
+
        /*
         * Set all global interrupts to be level triggered, active low.
         */
@@ -343,6 +346,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
        writel_relaxed(1, base + GIC_CPU_CTRL);
 }
 
+#ifdef CONFIG_CPU_PM
+/*
+ * Saves the GIC distributor registers during suspend or idle.  Must be called
+ * with interrupts disabled but before powering down the GIC.  After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+static void gic_dist_save(unsigned int gic_nr)
+{
+       unsigned int gic_irqs;
+       void __iomem *dist_base;
+       int i;
+
+       if (gic_nr >= MAX_GIC_NR)
+               BUG();
+
+       gic_irqs = gic_data[gic_nr].gic_irqs;
+       dist_base = gic_data[gic_nr].dist_base;
+
+       if (!dist_base)
+               return;
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+               gic_data[gic_nr].saved_spi_conf[i] =
+                       readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+               gic_data[gic_nr].saved_spi_target[i] =
+                       readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+               gic_data[gic_nr].saved_spi_enable[i] =
+                       readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle.  Must be called before enabling interrupts.  If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+static void gic_dist_restore(unsigned int gic_nr)
+{
+       unsigned int gic_irqs;
+       unsigned int i;
+       void __iomem *dist_base;
+
+       if (gic_nr >= MAX_GIC_NR)
+               BUG();
+
+       gic_irqs = gic_data[gic_nr].gic_irqs;
+       dist_base = gic_data[gic_nr].dist_base;
+
+       if (!dist_base)
+               return;
+
+       writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+               writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
+                       dist_base + GIC_DIST_CONFIG + i * 4);
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+               writel_relaxed(0xa0a0a0a0,
+                       dist_base + GIC_DIST_PRI + i * 4);
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+               writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
+                       dist_base + GIC_DIST_TARGET + i * 4);
+
+       for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+               writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
+                       dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+       writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+}
+
+static void gic_cpu_save(unsigned int gic_nr)
+{
+       int i;
+       u32 *ptr;
+       void __iomem *dist_base;
+       void __iomem *cpu_base;
+
+       if (gic_nr >= MAX_GIC_NR)
+               BUG();
+
+       dist_base = gic_data[gic_nr].dist_base;
+       cpu_base = gic_data[gic_nr].cpu_base;
+
+       if (!dist_base || !cpu_base)
+               return;
+
+       ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+       for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+               ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+       ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+       for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+               ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+}
+
+static void gic_cpu_restore(unsigned int gic_nr)
+{
+       int i;
+       u32 *ptr;
+       void __iomem *dist_base;
+       void __iomem *cpu_base;
+
+       if (gic_nr >= MAX_GIC_NR)
+               BUG();
+
+       dist_base = gic_data[gic_nr].dist_base;
+       cpu_base = gic_data[gic_nr].cpu_base;
+
+       if (!dist_base || !cpu_base)
+               return;
+
+       ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+       for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+               writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+       ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+       for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+               writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
+
+       for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+               writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
+
+       writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
+       writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+}
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,        void *v)
+{
+       int i;
+
+       for (i = 0; i < MAX_GIC_NR; i++) {
+               switch (cmd) {
+               case CPU_PM_ENTER:
+                       gic_cpu_save(i);
+                       break;
+               case CPU_PM_ENTER_FAILED:
+               case CPU_PM_EXIT:
+                       gic_cpu_restore(i);
+                       break;
+               case CPU_CLUSTER_PM_ENTER:
+                       gic_dist_save(i);
+                       break;
+               case CPU_CLUSTER_PM_ENTER_FAILED:
+               case CPU_CLUSTER_PM_EXIT:
+                       gic_dist_restore(i);
+                       break;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block gic_notifier_block = {
+       .notifier_call = gic_notifier,
+};
+
+static void __init gic_pm_init(struct gic_chip_data *gic)
+{
+       gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
+               sizeof(u32));
+       BUG_ON(!gic->saved_ppi_enable);
+
+       gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
+               sizeof(u32));
+       BUG_ON(!gic->saved_ppi_conf);
+
+       cpu_pm_register_notifier(&gic_notifier_block);
+}
+#else
+static void __init gic_pm_init(struct gic_chip_data *gic)
+{
+}
+#endif
+
 void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
        void __iomem *dist_base, void __iomem *cpu_base)
 {
@@ -358,8 +544,10 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
        if (gic_nr == 0)
                gic_cpu_base_addr = cpu_base;
 
+       gic_chip.flags |= gic_arch_extn.flags;
        gic_dist_init(gic, irq_start);
        gic_cpu_init(gic);
+       gic_pm_init(gic);
 }
 
 void __cpuinit gic_secondary_init(unsigned int gic_nr)
index 7a21d0b..7f27fab 100644 (file)
@@ -205,6 +205,13 @@ extern void *dma_alloc_writecombine(struct device *, size_t, dma_addr_t *,
 int dma_mmap_writecombine(struct device *, struct vm_area_struct *,
                void *, dma_addr_t, size_t);
 
+/*
+ * This can be called during boot to increase the size of the consistent
+ * DMA region above it's default value of 2MB. It must be called before the
+ * memory allocator is initialised, i.e. before any core_initcall.
+ */
+extern void __init init_consistent_dma_size(unsigned long size);
+
 
 #ifdef CONFIG_DMABOUNCE
 /*
index 435d3f8..c562705 100644 (file)
@@ -46,6 +46,14 @@ struct gic_chip_data {
        unsigned int irq_offset;
        void __iomem *dist_base;
        void __iomem *cpu_base;
+#ifdef CONFIG_CPU_PM
+       u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
+       u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
+       u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
+       u32 __percpu *saved_ppi_enable;
+       u32 __percpu *saved_ppi_conf;
+#endif
+       unsigned int gic_irqs;
 };
 #endif
 
index f389b27..c190bc9 100644 (file)
@@ -50,6 +50,7 @@ static inline void decode_ctrl_reg(u32 reg,
 #define ARM_DEBUG_ARCH_V6_1    2
 #define ARM_DEBUG_ARCH_V7_ECP14        3
 #define ARM_DEBUG_ARCH_V7_MM   4
+#define ARM_DEBUG_ARCH_V7_1    5
 
 /* Breakpoint */
 #define ARM_BREAKPOINT_EXECUTE 0
@@ -57,6 +58,7 @@ static inline void decode_ctrl_reg(u32 reg,
 /* Watchpoints */
 #define ARM_BREAKPOINT_LOAD    1
 #define ARM_BREAKPOINT_STORE   2
+#define ARM_FSR_ACCESS_MASK    (1 << 11)
 
 /* Privilege Levels */
 #define ARM_BREAKPOINT_PRIV    1
index 217aa19..727da11 100644 (file)
@@ -17,7 +17,7 @@ struct sys_timer;
 struct machine_desc {
        unsigned int            nr;             /* architecture number  */
        const char              *name;          /* architecture name    */
-       unsigned long           boot_params;    /* tagged list          */
+       unsigned long           atag_offset;    /* tagged list (relative) */
        const char              **dt_compat;    /* array of device tree
                                                 * 'compatible' strings */
 
index d2fedb5..b36f365 100644 (file)
@@ -29,6 +29,7 @@ struct map_desc {
 #define MT_MEMORY_NONCACHED    11
 #define MT_MEMORY_DTCM         12
 #define MT_MEMORY_ITCM         13
+#define MT_MEMORY_SO           14
 
 #ifdef CONFIG_MMU
 extern void iotable_init(struct map_desc *, int);
index b8de516..652fccc 100644 (file)
  */
 #define IOREMAP_MAX_ORDER      24
 
-/*
- * Size of DMA-consistent memory region.  Must be multiple of 2M,
- * between 2MB and 14MB inclusive.
- */
-#ifndef CONSISTENT_DMA_SIZE
-#define CONSISTENT_DMA_SIZE    SZ_2M
-#endif
-
 #define CONSISTENT_END         (0xffe00000UL)
-#define CONSISTENT_BASE                (CONSISTENT_END - CONSISTENT_DMA_SIZE)
 
 #else /* CONFIG_MMU */
 
index 5750704..f1956b2 100644 (file)
@@ -232,6 +232,9 @@ extern pgprot_t             pgprot_kernel;
 #define pgprot_writecombine(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)
 
+#define pgprot_stronglyordered(prot) \
+       __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)
+
 #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
 #define pgprot_dmacoherent(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN)
index b7e82c4..71d99b8 100644 (file)
 #define __ARM_PMU_H__
 
 #include <linux/interrupt.h>
+#include <linux/perf_event.h>
 
+/*
+ * Types of PMUs that can be accessed directly and require mutual
+ * exclusion between profiling tools.
+ */
 enum arm_pmu_type {
        ARM_PMU_DEVICE_CPU      = 0,
        ARM_NUM_PMU_DEVICES,
@@ -37,21 +42,17 @@ struct arm_pmu_platdata {
  * reserve_pmu() - reserve the hardware performance counters
  *
  * Reserve the hardware performance counters in the system for exclusive use.
- * The platform_device for the system is returned on success, ERR_PTR()
- * encoded error on failure.
+ * Returns 0 on success or -EBUSY if the lock is already held.
  */
-extern struct platform_device *
+extern int
 reserve_pmu(enum arm_pmu_type type);
 
 /**
  * release_pmu() - Relinquish control of the performance counters
  *
  * Release the performance counters and allow someone else to use them.
- * Callers must have disabled the counters and released IRQs before calling
- * this. The platform_device returned from reserve_pmu() must be passed as
- * a cookie.
  */
-extern int
+extern void
 release_pmu(enum arm_pmu_type type);
 
 /**
@@ -68,24 +69,78 @@ init_pmu(enum arm_pmu_type type);
 
 #include <linux/err.h>
 
-static inline struct platform_device *
-reserve_pmu(enum arm_pmu_type type)
-{
-       return ERR_PTR(-ENODEV);
-}
-
 static inline int
-release_pmu(enum arm_pmu_type type)
+reserve_pmu(enum arm_pmu_type type)
 {
        return -ENODEV;
 }
 
-static inline int
-init_pmu(enum arm_pmu_type type)
-{
-       return -ENODEV;
-}
+static inline void
+release_pmu(enum arm_pmu_type type)    { }
 
 #endif /* CONFIG_CPU_HAS_PMU */
 
+#ifdef CONFIG_HW_PERF_EVENTS
+
+/* The events for a given PMU register set. */
+struct pmu_hw_events {
+       /*
+        * The events that are active on the PMU for the given index.
+        */
+       struct perf_event       **events;
+
+       /*
+        * A 1 bit for an index indicates that the counter is being used for
+        * an event. A 0 means that the counter can be used.
+        */
+       unsigned long           *used_mask;
+
+       /*
+        * Hardware lock to serialize accesses to PMU registers. Needed for the
+        * read/modify/write sequences.
+        */
+       raw_spinlock_t          pmu_lock;
+};
+
+struct arm_pmu {
+       struct pmu      pmu;
+       enum arm_perf_pmu_ids id;
+       enum arm_pmu_type type;
+       cpumask_t       active_irqs;
+       const char      *name;
+       irqreturn_t     (*handle_irq)(int irq_num, void *dev);
+       void            (*enable)(struct hw_perf_event *evt, int idx);
+       void            (*disable)(struct hw_perf_event *evt, int idx);
+       int             (*get_event_idx)(struct pmu_hw_events *hw_events,
+                                        struct hw_perf_event *hwc);
+       int             (*set_event_filter)(struct hw_perf_event *evt,
+                                           struct perf_event_attr *attr);
+       u32             (*read_counter)(int idx);
+       void            (*write_counter)(int idx, u32 val);
+       void            (*start)(void);
+       void            (*stop)(void);
+       void            (*reset)(void *);
+       int             (*map_event)(struct perf_event *event);
+       int             num_events;
+       atomic_t        active_events;
+       struct mutex    reserve_mutex;
+       u64             max_period;
+       struct platform_device  *plat_device;
+       struct pmu_hw_events    *(*get_hw_events)(void);
+};
+
+#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
+
+int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type);
+
+u64 armpmu_event_update(struct perf_event *event,
+                       struct hw_perf_event *hwc,
+                       int idx, int overflow);
+
+int armpmu_event_set_period(struct perf_event *event,
+                           struct hw_perf_event *hwc,
+                           int idx);
+
+#endif /* CONFIG_HW_PERF_EVENTS */
+
 #endif /* __ARM_PMU_H__ */
index 633d1cb..9e92cb2 100644 (file)
@@ -81,6 +81,10 @@ extern void cpu_dcache_clean_area(void *, int);
 extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
 extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte, unsigned int ext);
 extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
+
+/* These three are private to arch/arm/kernel/suspend.c */
+extern void cpu_do_suspend(void *);
+extern void cpu_do_resume(void *);
 #else
 #define cpu_proc_init                  processor._proc_init
 #define cpu_proc_fin                   processor._proc_fin
@@ -89,6 +93,10 @@ extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
 #define cpu_dcache_clean_area          processor.dcache_clean_area
 #define cpu_set_pte_ext                        processor.set_pte_ext
 #define cpu_do_switch_mm               processor.switch_mm
+
+/* These three are private to arch/arm/kernel/suspend.c */
+#define cpu_do_suspend                 processor.do_suspend
+#define cpu_do_resume                  processor.do_resume
 #endif
 
 extern void cpu_resume(void);
index b0e4e1a..1c0a551 100644 (file)
@@ -1,22 +1,7 @@
 #ifndef __ASM_ARM_SUSPEND_H
 #define __ASM_ARM_SUSPEND_H
 
-#include <asm/memory.h>
-#include <asm/tlbflush.h>
-
 extern void cpu_resume(void);
-
-/*
- * Hide the first two arguments to __cpu_suspend - these are an implementation
- * detail which platform code shouldn't have to know about.
- */
-static inline int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
-{
-       extern int __cpu_suspend(int, long, unsigned long,
-                                int (*)(unsigned long));
-       int ret = __cpu_suspend(0, PHYS_OFFSET - PAGE_OFFSET, arg, fn);
-       flush_tlb_all();
-       return ret;
-}
+extern int cpu_suspend(unsigned long, int (*)(unsigned long));
 
 #endif
index f7887dc..8fa83f5 100644 (file)
@@ -29,7 +29,7 @@ obj-$(CONFIG_MODULES)         += armksyms.o module.o
 obj-$(CONFIG_ARTHUR)           += arthur.o
 obj-$(CONFIG_ISA_DMA)          += dma-isa.o
 obj-$(CONFIG_PCI)              += bios32.o isa.o
-obj-$(CONFIG_PM_SLEEP)         += sleep.o
+obj-$(CONFIG_PM_SLEEP)         += sleep.o suspend.o
 obj-$(CONFIG_HAVE_SCHED_CLOCK) += sched_clock.o
 obj-$(CONFIG_SMP)              += smp.o smp_tlb.o
 obj-$(CONFIG_HAVE_ARM_SCU)     += smp_scu.o
@@ -43,6 +43,13 @@ obj-$(CONFIG_KPROBES)                += kprobes-thumb.o
 else
 obj-$(CONFIG_KPROBES)          += kprobes-arm.o
 endif
+obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
+test-kprobes-objs              := kprobes-test.o
+ifdef CONFIG_THUMB2_KERNEL
+test-kprobes-objs              += kprobes-test-thumb.o
+else
+test-kprobes-objs              += kprobes-test-arm.o
+endif
 obj-$(CONFIG_ATAGS_PROC)       += atags.o
 obj-$(CONFIG_OABI_COMPAT)      += sys_oabi-compat.o
 obj-$(CONFIG_ARM_THUMBEE)      += thumbee.o
index a927ca1..5a46225 100644 (file)
@@ -45,7 +45,6 @@ static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[ARM_MAX_WRP]);
 
 /* Number of BRP/WRP registers on this CPU. */
 static int core_num_brps;
-static int core_num_reserved_brps;
 static int core_num_wrps;
 
 /* Debug architecture version. */
@@ -137,10 +136,11 @@ static u8 get_debug_arch(void)
        u32 didr;
 
        /* Do we implement the extended CPUID interface? */
-       if (WARN_ONCE((((read_cpuid_id() >> 16) & 0xf) != 0xf),
-           "CPUID feature registers not supported. "
-           "Assuming v6 debug is present.\n"))
+       if (((read_cpuid_id() >> 16) & 0xf) != 0xf) {
+               pr_warning("CPUID feature registers not supported. "
+                          "Assuming v6 debug is present.\n");
                return ARM_DEBUG_ARCH_V6;
+       }
 
        ARM_DBG_READ(c0, 0, didr);
        return (didr >> 16) & 0xf;
@@ -154,10 +154,21 @@ u8 arch_get_debug_arch(void)
 static int debug_arch_supported(void)
 {
        u8 arch = get_debug_arch();
-       return arch >= ARM_DEBUG_ARCH_V6 && arch <= ARM_DEBUG_ARCH_V7_ECP14;
+
+       /* We don't support the memory-mapped interface. */
+       return (arch >= ARM_DEBUG_ARCH_V6 && arch <= ARM_DEBUG_ARCH_V7_ECP14) ||
+               arch >= ARM_DEBUG_ARCH_V7_1;
+}
+
+/* Determine number of WRP registers available. */
+static int get_num_wrp_resources(void)
+{
+       u32 didr;
+       ARM_DBG_READ(c0, 0, didr);
+       return ((didr >> 28) & 0xf) + 1;
 }
 
-/* Determine number of BRP register available. */
+/* Determine number of BRP registers available. */
 static int get_num_brp_resources(void)
 {
        u32 didr;
@@ -176,9 +187,10 @@ static int core_has_mismatch_brps(void)
 static int get_num_wrps(void)
 {
        /*
-        * FIXME: When a watchpoint fires, the only way to work out which
-        * watchpoint it was is by disassembling the faulting instruction
-        * and working out the address of the memory access.
+        * On debug architectures prior to 7.1, when a watchpoint fires, the
+        * only way to work out which watchpoint it was is by disassembling
+        * the faulting instruction and working out the address of the memory
+        * access.
         *
         * Furthermore, we can only do this if the watchpoint was precise
         * since imprecise watchpoints prevent us from calculating register
@@ -192,36 +204,17 @@ static int get_num_wrps(void)
         * [the ARM ARM states that the DFAR is UNKNOWN, but experience shows
         * that it is set on some implementations].
         */
+       if (get_debug_arch() < ARM_DEBUG_ARCH_V7_1)
+               return 1;
 
-#if 0
-       int wrps;
-       u32 didr;
-       ARM_DBG_READ(c0, 0, didr);
-       wrps = ((didr >> 28) & 0xf) + 1;
-#endif
-       int wrps = 1;
-
-       if (core_has_mismatch_brps() && wrps >= get_num_brp_resources())
-               wrps = get_num_brp_resources() - 1;
-
-       return wrps;
-}
-
-/* We reserve one breakpoint for each watchpoint. */
-static int get_num_reserved_brps(void)
-{
-       if (core_has_mismatch_brps())
-               return get_num_wrps();
-       return 0;
+       return get_num_wrp_resources();
 }
 
 /* Determine number of usable BRPs available. */
 static int get_num_brps(void)
 {
        int brps = get_num_brp_resources();
-       if (core_has_mismatch_brps())
-               brps -= get_num_reserved_brps();
-       return brps;
+       return core_has_mismatch_brps() ? brps - 1 : brps;
 }
 
 /*
@@ -239,7 +232,7 @@ static int enable_monitor_mode(void)
 
        /* Ensure that halting mode is disabled. */
        if (WARN_ONCE(dscr & ARM_DSCR_HDBGEN,
-                       "halting debug mode enabled. Unable to access hardware resources.\n")) {
+               "halting debug mode enabled. Unable to access hardware resources.\n")) {
                ret = -EPERM;
                goto out;
        }
@@ -255,6 +248,7 @@ static int enable_monitor_mode(void)
                ARM_DBG_WRITE(c1, 0, (dscr | ARM_DSCR_MDBGEN));
                break;
        case ARM_DEBUG_ARCH_V7_ECP14:
+       case ARM_DEBUG_ARCH_V7_1:
                ARM_DBG_WRITE(c2, 2, (dscr | ARM_DSCR_MDBGEN));
                break;
        default:
@@ -346,24 +340,10 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
                val_base = ARM_BASE_BVR;
                slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
                max_slots = core_num_brps;
-               if (info->step_ctrl.enabled) {
-                       /* Override the breakpoint data with the step data. */
-                       addr = info->trigger & ~0x3;
-                       ctrl = encode_ctrl_reg(info->step_ctrl);
-               }
        } else {
                /* Watchpoint */
-               if (info->step_ctrl.enabled) {
-                       /* Install into the reserved breakpoint region. */
-                       ctrl_base = ARM_BASE_BCR + core_num_brps;
-                       val_base = ARM_BASE_BVR + core_num_brps;
-                       /* Override the watchpoint data with the step data. */
-                       addr = info->trigger & ~0x3;
-                       ctrl = encode_ctrl_reg(info->step_ctrl);
-               } else {
-                       ctrl_base = ARM_BASE_WCR;
-                       val_base = ARM_BASE_WVR;
-               }
+               ctrl_base = ARM_BASE_WCR;
+               val_base = ARM_BASE_WVR;
                slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
                max_slots = core_num_wrps;
        }
@@ -382,6 +362,17 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
                goto out;
        }
 
+       /* Override the breakpoint data with the step data. */
+       if (info->step_ctrl.enabled) {
+               addr = info->trigger & ~0x3;
+               ctrl = encode_ctrl_reg(info->step_ctrl);
+               if (info->ctrl.type != ARM_BREAKPOINT_EXECUTE) {
+                       i = 0;
+                       ctrl_base = ARM_BASE_BCR + core_num_brps;
+                       val_base = ARM_BASE_BVR + core_num_brps;
+               }
+       }
+
        /* Setup the address register. */
        write_wb_reg(val_base + i, addr);
 
@@ -405,10 +396,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
                max_slots = core_num_brps;
        } else {
                /* Watchpoint */
-               if (info->step_ctrl.enabled)
-                       base = ARM_BASE_BCR + core_num_brps;
-               else
-                       base = ARM_BASE_WCR;
+               base = ARM_BASE_WCR;
                slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
                max_slots = core_num_wrps;
        }
@@ -426,6 +414,13 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
        if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot\n"))
                return;
 
+       /* Ensure that we disable the mismatch breakpoint. */
+       if (info->ctrl.type != ARM_BREAKPOINT_EXECUTE &&
+           info->step_ctrl.enabled) {
+               i = 0;
+               base = ARM_BASE_BCR + core_num_brps;
+       }
+
        /* Reset the control register. */
        write_wb_reg(base + i, 0);
 }
@@ -632,10 +627,9 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
         * we can use the mismatch feature as a poor-man's hardware
         * single-step, but this only works for per-task breakpoints.
         */
-       if (WARN_ONCE(!bp->overflow_handler &&
-               (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps()
-                || !bp->hw.bp_target),
-                       "overflow handler required but none found\n")) {
+       if (!bp->overflow_handler && (arch_check_bp_in_kernelspace(bp) ||
+           !core_has_mismatch_brps() || !bp->hw.bp_target)) {
+               pr_warning("overflow handler required but none found\n");
                ret = -EINVAL;
        }
 out:
@@ -666,34 +660,62 @@ static void disable_single_step(struct perf_event *bp)
        arch_install_hw_breakpoint(bp);
 }
 
-static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
+static void watchpoint_handler(unsigned long addr, unsigned int fsr,
+                              struct pt_regs *regs)
 {
-       int i;
+       int i, access;
+       u32 val, ctrl_reg, alignment_mask;
        struct perf_event *wp, **slots;
        struct arch_hw_breakpoint *info;
+       struct arch_hw_breakpoint_ctrl ctrl;
 
        slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
 
-       /* Without a disassembler, we can only handle 1 watchpoint. */
-       BUG_ON(core_num_wrps > 1);
-
        for (i = 0; i < core_num_wrps; ++i) {
                rcu_read_lock();
 
                wp = slots[i];
 
-               if (wp == NULL) {
-                       rcu_read_unlock();
-                       continue;
-               }
+               if (wp == NULL)
+                       goto unlock;
 
+               info = counter_arch_bp(wp);
                /*
-                * The DFAR is an unknown value. Since we only allow a
-                * single watchpoint, we can set the trigger to the lowest
-                * possible faulting address.
+                * The DFAR is an unknown value on debug architectures prior
+                * to 7.1. Since we only allow a single watchpoint on these
+                * older CPUs, we can set the trigger to the lowest possible
+                * faulting address.
                 */
-               info = counter_arch_bp(wp);
-               info->trigger = wp->attr.bp_addr;
+               if (debug_arch < ARM_DEBUG_ARCH_V7_1) {
+                       BUG_ON(i > 0);
+                       info->trigger = wp->attr.bp_addr;
+               } else {
+                       if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
+                               alignment_mask = 0x7;
+                       else
+                               alignment_mask = 0x3;
+
+                       /* Check if the watchpoint value matches. */
+                       val = read_wb_reg(ARM_BASE_WVR + i);
+                       if (val != (addr & ~alignment_mask))
+                               goto unlock;
+
+                       /* Possible match, check the byte address select. */
+                       ctrl_reg = read_wb_reg(ARM_BASE_WCR + i);
+                       decode_ctrl_reg(ctrl_reg, &ctrl);
+                       if (!((1 << (addr & alignment_mask)) & ctrl.len))
+                               goto unlock;
+
+                       /* Check that the access type matches. */
+                       access = (fsr & ARM_FSR_ACCESS_MASK) ? HW_BREAKPOINT_W :
+                                HW_BREAKPOINT_R;
+                       if (!(access & hw_breakpoint_type(wp)))
+                               goto unlock;
+
+                       /* We have a winner. */
+                       info->trigger = addr;
+               }
+
                pr_debug("watchpoint fired: address = 0x%x\n", info->trigger);
                perf_bp_event(wp, regs);
 
@@ -705,6 +727,7 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
                if (!wp->overflow_handler)
                        enable_single_step(wp, instruction_pointer(regs));
 
+unlock:
                rcu_read_unlock();
        }
 }
@@ -717,7 +740,7 @@ static void watchpoint_single_step_handler(unsigned long pc)
 
        slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
 
-       for (i = 0; i < core_num_reserved_brps; ++i) {
+       for (i = 0; i < core_num_wrps; ++i) {
                rcu_read_lock();
 
                wp = slots[i];
@@ -820,7 +843,7 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
        case ARM_ENTRY_ASYNC_WATCHPOINT:
                WARN(1, "Asynchronous watchpoint exception taken. Debugging results may be unreliable\n");
        case ARM_ENTRY_SYNC_WATCHPOINT:
-               watchpoint_handler(addr, regs);
+               watchpoint_handler(addr, fsr, regs);
                break;
        default:
                ret = 1; /* Unhandled fault. */
@@ -834,11 +857,31 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
 /*
  * One-time initialisation.
  */
-static void reset_ctrl_regs(void *info)
+static cpumask_t debug_err_mask;
+
+static int debug_reg_trap(struct pt_regs *regs, unsigned int instr)
 {
-       int i, cpu = smp_processor_id();
+       int cpu = smp_processor_id();
+
+       pr_warning("Debug register access (0x%x) caused undefined instruction on CPU %d\n",
+                  instr, cpu);
+
+       /* Set the error flag for this CPU and skip the faulting instruction. */
+       cpumask_set_cpu(cpu, &debug_err_mask);
+       instruction_pointer(regs) += 4;
+       return 0;
+}
+
+static struct undef_hook debug_reg_hook = {
+       .instr_mask     = 0x0fe80f10,
+       .instr_val      = 0x0e000e10,
+       .fn             = debug_reg_trap,
+};
+
+static void reset_ctrl_regs(void *unused)
+{
+       int i, raw_num_brps, err = 0, cpu = smp_processor_id();
        u32 dbg_power;
-       cpumask_t *cpumask = info;
 
        /*
         * v7 debug contains save and restore registers so that debug state
@@ -848,38 +891,52 @@ static void reset_ctrl_regs(void *info)
         * Access Register to avoid taking undefined instruction exceptions
         * later on.
         */
-       if (debug_arch >= ARM_DEBUG_ARCH_V7_ECP14) {
+       switch (debug_arch) {
+       case ARM_DEBUG_ARCH_V7_ECP14:
                /*
                 * Ensure sticky power-down is clear (i.e. debug logic is
                 * powered up).
                 */
                asm volatile("mrc p14, 0, %0, c1, c5, 4" : "=r" (dbg_power));
-               if ((dbg_power & 0x1) == 0) {
-                       pr_warning("CPU %d debug is powered down!\n", cpu);
-                       cpumask_or(cpumask, cpumask, cpumask_of(cpu));
-                       return;
-               }
-
+               if ((dbg_power & 0x1) == 0)
+                       err = -EPERM;
+               break;
+       case ARM_DEBUG_ARCH_V7_1:
                /*
-                * Unconditionally clear the lock by writing a value
-                * other than 0xC5ACCE55 to the access register.
+                * Ensure the OS double lock is clear.
                 */
-               asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0));
-               isb();
+               asm volatile("mrc p14, 0, %0, c1, c3, 4" : "=r" (dbg_power));
+               if ((dbg_power & 0x1) == 1)
+                       err = -EPERM;
+               break;
+       }
 
-               /*
-                * Clear any configured vector-catch events before
-                * enabling monitor mode.
-                */
-               asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0));
-               isb();
+       if (err) {
+               pr_warning("CPU %d debug is powered down!\n", cpu);
+               cpumask_or(&debug_err_mask, &debug_err_mask, cpumask_of(cpu));
+               return;
        }
 
+       /*
+        * Unconditionally clear the lock by writing a value
+        * other than 0xC5ACCE55 to the access register.
+        */
+       asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0));
+       isb();
+
+       /*
+        * Clear any configured vector-catch events before
+        * enabling monitor mode.
+        */
+       asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0));
+       isb();
+
        if (enable_monitor_mode())
                return;
 
        /* We must also reset any reserved registers. */
-       for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) {
+       raw_num_brps = get_num_brp_resources();
+       for (i = 0; i < raw_num_brps; ++i) {
                write_wb_reg(ARM_BASE_BCR + i, 0UL);
                write_wb_reg(ARM_BASE_BVR + i, 0UL);
        }
@@ -895,6 +952,7 @@ static int __cpuinit dbg_reset_notify(struct notifier_block *self,
 {
        if (action == CPU_ONLINE)
                smp_call_function_single((int)cpu, reset_ctrl_regs, NULL, 1);
+
        return NOTIFY_OK;
 }
 
@@ -905,7 +963,6 @@ static struct notifier_block __cpuinitdata dbg_reset_nb = {
 static int __init arch_hw_breakpoint_init(void)
 {
        u32 dscr;
-       cpumask_t cpumask = { CPU_BITS_NONE };
 
        debug_arch = get_debug_arch();
 
@@ -916,28 +973,31 @@ static int __init arch_hw_breakpoint_init(void)
 
        /* Determine how many BRPs/WRPs are available. */
        core_num_brps = get_num_brps();
-       core_num_reserved_brps = get_num_reserved_brps();
        core_num_wrps = get_num_wrps();
 
-       pr_info("found %d breakpoint and %d watchpoint registers.\n",
-               core_num_brps + core_num_reserved_brps, core_num_wrps);
-
-       if (core_num_reserved_brps)
-               pr_info("%d breakpoint(s) reserved for watchpoint "
-                               "single-step.\n", core_num_reserved_brps);
+       /*
+        * We need to tread carefully here because DBGSWENABLE may be
+        * driven low on this core and there isn't an architected way to
+        * determine that.
+        */
+       register_undef_hook(&debug_reg_hook);
 
        /*
         * Reset the breakpoint resources. We assume that a halting
         * debugger will leave the world in a nice state for us.
         */
-       on_each_cpu(reset_ctrl_regs, &cpumask, 1);
-       if (!cpumask_empty(&cpumask)) {
+       on_each_cpu(reset_ctrl_regs, NULL, 1);
+       unregister_undef_hook(&debug_reg_hook);
+       if (!cpumask_empty(&debug_err_mask)) {
                core_num_brps = 0;
-               core_num_reserved_brps = 0;
                core_num_wrps = 0;
                return 0;
        }
 
+       pr_info("found %d " "%s" "breakpoint and %d watchpoint registers.\n",
+               core_num_brps, core_has_mismatch_brps() ? "(+1 reserved) " :
+               "", core_num_wrps);
+
        ARM_DBG_READ(c1, 0, dscr);
        if (dscr & ARM_DSCR_HDBGEN) {
                max_watchpoint_len = 4;
index 79203ee..9fe8910 100644 (file)
@@ -60,6 +60,7 @@
 
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
+#include <linux/module.h>
 
 #include "kprobes.h"
 
@@ -971,6 +972,9 @@ const union decode_item kprobe_decode_arm_table[] = {
 
        DECODE_END
 };
+#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
+EXPORT_SYMBOL_GPL(kprobe_decode_arm_table);
+#endif
 
 static void __kprobes arm_singlestep(struct kprobe *p, struct pt_regs *regs)
 {
diff --git a/arch/arm/kernel/kprobes-test-arm.c b/arch/arm/kernel/kprobes-test-arm.c
new file mode 100644 (file)
index 0000000..fc82de8
--- /dev/null
@@ -0,0 +1,1323 @@
+/*
+ * arch/arm/kernel/kprobes-test-arm.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "kprobes-test.h"
+
+
+#define TEST_ISA "32"
+
+#define TEST_ARM_TO_THUMB_INTERWORK_R(code1, reg, val, code2)  \
+       TESTCASE_START(code1 #reg code2)                        \
+       TEST_ARG_REG(reg, val)                                  \
+       TEST_ARG_REG(14, 99f)                                   \
+       TEST_ARG_END("")                                        \
+       "50:    nop                     \n\t"                   \
+       "1:     "code1 #reg code2"      \n\t"                   \
+       "       bx      lr              \n\t"                   \
+       ".thumb                         \n\t"                   \
+       "3:     adr     lr, 2f          \n\t"                   \
+       "       bx      lr              \n\t"                   \
+       ".arm                           \n\t"                   \
+       "2:     nop                     \n\t"                   \
+       TESTCASE_END
+
+#define TEST_ARM_TO_THUMB_INTERWORK_P(code1, reg, val, code2)  \
+       TESTCASE_START(code1 #reg code2)                        \
+       TEST_ARG_PTR(reg, val)                                  \
+       TEST_ARG_REG(14, 99f)                                   \
+       TEST_ARG_MEM(15, 3f+1)                                  \
+       TEST_ARG_END("")                                        \
+       "50:    nop                     \n\t"                   \
+       "1:     "code1 #reg code2"      \n\t"                   \
+       "       bx      lr              \n\t"                   \
+       ".thumb                         \n\t"                   \
+       "3:     adr     lr, 2f          \n\t"                   \
+       "       bx      lr              \n\t"                   \
+       ".arm                           \n\t"                   \
+       "2:     nop                     \n\t"                   \
+       TESTCASE_END
+
+
+void kprobe_arm_test_cases(void)
+{
+       kprobe_test_flags = 0;
+
+       TEST_GROUP("Data-processing (register), (register-shifted register), (immediate)")
+
+#define _DATA_PROCESSING_DNM(op,s,val)                                         \
+       TEST_RR(  op "eq" s "   r0,  r",1, VAL1,", r",2, val, "")               \
+       TEST_RR(  op "ne" s "   r1,  r",1, VAL1,", r",2, val, ", lsl #3")       \
+       TEST_RR(  op "cs" s "   r2,  r",3, VAL1,", r",2, val, ", lsr #4")       \
+       TEST_RR(  op "cc" s "   r3,  r",3, VAL1,", r",2, val, ", asr #5")       \
+       TEST_RR(  op "mi" s "   r4,  r",5, VAL1,", r",2, N(val),", asr #6")     \
+       TEST_RR(  op "pl" s "   r5,  r",5, VAL1,", r",2, val, ", ror #7")       \
+       TEST_RR(  op "vs" s "   r6,  r",7, VAL1,", r",2, val, ", rrx")          \
+       TEST_R(   op "vc" s "   r6,  r",7, VAL1,", pc, lsl #3")                 \
+       TEST_R(   op "vc" s "   r6,  r",7, VAL1,", sp, lsr #4")                 \
+       TEST_R(   op "vc" s "   r6,  pc, r",7, VAL1,", asr #5")                 \
+       TEST_R(   op "vc" s "   r6,  sp, r",7, VAL1,", ror #6")                 \
+       TEST_RRR( op "hi" s "   r8,  r",9, VAL1,", r",14,val, ", lsl r",0, 3,"")\
+       TEST_RRR( op "ls" s "   r9,  r",9, VAL1,", r",14,val, ", lsr r",7, 4,"")\
+       TEST_RRR( op "ge" s "   r10, r",11,VAL1,", r",14,val, ", asr r",7, 5,"")\
+       TEST_RRR( op "lt" s "   r11, r",11,VAL1,", r",14,N(val),", asr r",7, 6,"")\
+       TEST_RR(  op "gt" s "   r12, r13"       ", r",14,val, ", ror r",14,7,"")\
+       TEST_RR(  op "le" s "   r14, r",0, val, ", r13"       ", lsl r",14,8,"")\
+       TEST_RR(  op s "        r12, pc"        ", r",14,val, ", ror r",14,7,"")\
+       TEST_RR(  op s "        r14, r",0, val, ", pc"        ", lsl r",14,8,"")\
+       TEST_R(   op "eq" s "   r0,  r",11,VAL1,", #0xf5")                      \
+       TEST_R(   op "ne" s "   r11, r",0, VAL1,", #0xf5000000")                \
+       TEST_R(   op s "        r7,  r",8, VAL2,", #0x000af000")                \
+       TEST(     op s "        r4,  pc"        ", #0x00005a00")
+
+#define DATA_PROCESSING_DNM(op,val)            \
+       _DATA_PROCESSING_DNM(op,"",val)         \
+       _DATA_PROCESSING_DNM(op,"s",val)
+
+#define DATA_PROCESSING_NM(op,val)                                             \
+       TEST_RR(  op "ne        r",1, VAL1,", r",2, val, "")                    \
+       TEST_RR(  op "eq        r",1, VAL1,", r",2, val, ", lsl #3")            \
+       TEST_RR(  op "cc        r",3, VAL1,", r",2, val, ", lsr #4")            \
+       TEST_RR(  op "cs        r",3, VAL1,", r",2, val, ", asr #5")            \
+       TEST_RR(  op "pl        r",5, VAL1,", r",2, N(val),", asr #6")          \
+       TEST_RR(  op "mi        r",5, VAL1,", r",2, val, ", ror #7")            \
+       TEST_RR(  op "vc        r",7, VAL1,", r",2, val, ", rrx")               \
+       TEST_R (  op "vs        r",7, VAL1,", pc, lsl #3")                      \
+       TEST_R (  op "vs        r",7, VAL1,", sp, lsr #4")                      \
+       TEST_R(   op "vs        pc, r",7, VAL1,", asr #5")                      \
+       TEST_R(   op "vs        sp, r",7, VAL1,", ror #6")                      \
+       TEST_RRR( op "ls        r",9, VAL1,", r",14,val, ", lsl r",0, 3,"")     \
+       TEST_RRR( op "hi        r",9, VAL1,", r",14,val, ", lsr r",7, 4,"")     \
+       TEST_RRR( op "lt        r",11,VAL1,", r",14,val, ", asr r",7, 5,"")     \
+       TEST_RRR( op "ge        r",11,VAL1,", r",14,N(val),", asr r",7, 6,"")   \
+       TEST_RR(  op "le        r13"       ", r",14,val, ", ror r",14,7,"")     \
+       TEST_RR(  op "gt        r",0, val, ", r13"       ", lsl r",14,8,"")     \
+       TEST_RR(  op "  pc"        ", r",14,val, ", ror r",14,7,"")             \
+       TEST_RR(  op "  r",0, val, ", pc"        ", lsl r",14,8,"")             \
+       TEST_R(   op "eq        r",11,VAL1,", #0xf5")                           \
+       TEST_R(   op "ne        r",0, VAL1,", #0xf5000000")                     \
+       TEST_R(   op "  r",8, VAL2,", #0x000af000")
+
+#define _DATA_PROCESSING_DM(op,s,val)                                  \
+       TEST_R(   op "eq" s "   r0,  r",1, val, "")                     \
+       TEST_R(   op "ne" s "   r1,  r",1, val, ", lsl #3")             \
+       TEST_R(   op "cs" s "   r2,  r",3, val, ", lsr #4")             \
+       TEST_R(   op "cc" s "   r3,  r",3, val, ", asr #5")             \
+       TEST_R(   op "mi" s "   r4,  r",5, N(val),", asr #6")           \
+       TEST_R(   op "pl" s "   r5,  r",5, val, ", ror #7")             \
+       TEST_R(   op "vs" s "   r6,  r",10,val, ", rrx")                \
+       TEST(     op "vs" s "   r7,  pc, lsl #3")                       \
+       TEST(     op "vs" s "   r7,  sp, lsr #4")                       \
+       TEST_RR(  op "vc" s "   r8,  r",7, val, ", lsl r",0, 3,"")      \
+       TEST_RR(  op "hi" s "   r9,  r",9, val, ", lsr r",7, 4,"")      \
+       TEST_RR(  op "ls" s "   r10, r",9, val, ", asr r",7, 5,"")      \
+       TEST_RR(  op "ge" s "   r11, r",11,N(val),", asr r",7, 6,"")    \
+       TEST_RR(  op "lt" s "   r12, r",11,val, ", ror r",14,7,"")      \
+       TEST_R(   op "gt" s "   r14, r13"       ", lsl r",14,8,"")      \
+       TEST_R(   op "le" s "   r14, pc"        ", lsl r",14,8,"")      \
+       TEST(     op "eq" s "   r0,  #0xf5")                            \
+       TEST(     op "ne" s "   r11, #0xf5000000")                      \
+       TEST(     op s "        r7,  #0x000af000")                      \
+       TEST(     op s "        r4,  #0x00005a00")
+
+#define DATA_PROCESSING_DM(op,val)             \
+       _DATA_PROCESSING_DM(op,"",val)          \
+       _DATA_PROCESSING_DM(op,"s",val)
+
+       DATA_PROCESSING_DNM("and",0xf00f00ff)
+       DATA_PROCESSING_DNM("eor",0xf00f00ff)
+       DATA_PROCESSING_DNM("sub",VAL2)
+       DATA_PROCESSING_DNM("rsb",VAL2)
+       DATA_PROCESSING_DNM("add",VAL2)
+       DATA_PROCESSING_DNM("adc",VAL2)
+       DATA_PROCESSING_DNM("sbc",VAL2)
+       DATA_PROCESSING_DNM("rsc",VAL2)
+       DATA_PROCESSING_NM("tst",0xf00f00ff)
+       DATA_PROCESSING_NM("teq",0xf00f00ff)
+       DATA_PROCESSING_NM("cmp",VAL2)
+       DATA_PROCESSING_NM("cmn",VAL2)
+       DATA_PROCESSING_DNM("orr",0xf00f00ff)
+       DATA_PROCESSING_DM("mov",VAL2)
+       DATA_PROCESSING_DNM("bic",0xf00f00ff)
+       DATA_PROCESSING_DM("mvn",VAL2)
+
+       TEST("mov       ip, sp") /* This has special case emulation code */
+
+       TEST_SUPPORTED("mov     pc, #0x1000");
+       TEST_SUPPORTED("mov     sp, #0x1000");
+       TEST_SUPPORTED("cmp     pc, #0x1000");
+       TEST_SUPPORTED("cmp     sp, #0x1000");
+
+       /* Data-processing with PC as shift*/
+       TEST_UNSUPPORTED(".word 0xe15c0f1e      @ cmp   r12, r14, asl pc")
+       TEST_UNSUPPORTED(".word 0xe1a0cf1e      @ mov   r12, r14, asl pc")
+       TEST_UNSUPPORTED(".word 0xe08caf1e      @ add   r10, r12, r14, asl pc")
+
+       /* Data-processing with PC as shift*/
+       TEST_UNSUPPORTED("movs  pc, r1")
+       TEST_UNSUPPORTED("movs  pc, r1, lsl r2")
+       TEST_UNSUPPORTED("movs  pc, #0x10000")
+       TEST_UNSUPPORTED("adds  pc, lr, r1")
+       TEST_UNSUPPORTED("adds  pc, lr, r1, lsl r2")
+       TEST_UNSUPPORTED("adds  pc, lr, #4")
+
+       /* Data-processing with SP as target */
+       TEST("add       sp, sp, #16")
+       TEST("sub       sp, sp, #8")
+       TEST("bic       sp, sp, #0x20")
+       TEST("orr       sp, sp, #0x20")
+       TEST_PR( "add   sp, r",10,0,", r",11,4,"")
+       TEST_PRR("add   sp, r",10,0,", r",11,4,", asl r",12,1,"")
+       TEST_P(  "mov   sp, r",10,0,"")
+       TEST_PR( "mov   sp, r",10,0,", asl r",12,0,"")
+
+       /* Data-processing with PC as target */
+       TEST_BF(   "add pc, pc, #2f-1b-8")
+       TEST_BF_R ("add pc, pc, r",14,2f-1f-8,"")
+       TEST_BF_R ("add pc, r",14,2f-1f-8,", pc")
+       TEST_BF_R ("mov pc, r",0,2f,"")
+       TEST_BF_RR("mov pc, r",0,2f,", asl r",1,0,"")
+       TEST_BB(   "sub pc, pc, #1b-2b+8")
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_BB(   "sub pc, pc, #1b-2b+8-2") /* UNPREDICTABLE before ARMv6 */
+#endif
+       TEST_BB_R( "sub pc, pc, r",14, 1f-2f+8,"")
+       TEST_BB_R( "rsb pc, r",14,1f-2f+8,", pc")
+       TEST_RR(   "add pc, pc, r",10,-2,", asl r",11,1,"")
+#ifdef CONFIG_THUMB2_KERNEL
+       TEST_ARM_TO_THUMB_INTERWORK_R("add      pc, pc, r",0,3f-1f-8+1,"")
+       TEST_ARM_TO_THUMB_INTERWORK_R("sub      pc, r",0,3f+8+1,", #8")
+#endif
+       TEST_GROUP("Miscellaneous instructions")
+
+       TEST("mrs       r0, cpsr")
+       TEST("mrspl     r7, cpsr")
+       TEST("mrs       r14, cpsr")
+       TEST_UNSUPPORTED(".word 0xe10ff000      @ mrs r15, cpsr")
+       TEST_UNSUPPORTED("mrs   r0, spsr")
+       TEST_UNSUPPORTED("mrs   lr, spsr")
+
+       TEST_UNSUPPORTED("msr   cpsr, r0")
+       TEST_UNSUPPORTED("msr   cpsr_f, lr")
+       TEST_UNSUPPORTED("msr   spsr, r0")
+
+       TEST_BF_R("bx   r",0,2f,"")
+       TEST_BB_R("bx   r",7,2f,"")
+       TEST_BF_R("bxeq r",14,2f,"")
+
+       TEST_R("clz     r0, r",0, 0x0,"")
+       TEST_R("clzeq   r7, r",14,0x1,"")
+       TEST_R("clz     lr, r",7, 0xffffffff,"")
+       TEST(  "clz     r4, sp")
+       TEST_UNSUPPORTED(".word 0x016fff10      @ clz pc, r0")
+       TEST_UNSUPPORTED(".word 0x016f0f1f      @ clz r0, pc")
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_UNSUPPORTED("bxj   r0")
+#endif
+
+       TEST_BF_R("blx  r",0,2f,"")
+       TEST_BB_R("blx  r",7,2f,"")
+       TEST_BF_R("blxeq        r",14,2f,"")
+       TEST_UNSUPPORTED(".word 0x0120003f      @ blx pc")
+
+       TEST_RR(   "qadd        r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(   "qaddvs      lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_R(    "qadd        lr, r",9, VAL2,", r13")
+       TEST_RR(   "qsub        r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(   "qsubvs      lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_R(    "qsub        lr, r",9, VAL2,", r13")
+       TEST_RR(   "qdadd       r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(   "qdaddvs     lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_R(    "qdadd       lr, r",9, VAL2,", r13")
+       TEST_RR(   "qdsub       r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(   "qdsubvs     lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_R(    "qdsub       lr, r",9, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe101f050      @ qadd pc, r0, r1")
+       TEST_UNSUPPORTED(".word 0xe121f050      @ qsub pc, r0, r1")
+       TEST_UNSUPPORTED(".word 0xe141f050      @ qdadd pc, r0, r1")
+       TEST_UNSUPPORTED(".word 0xe161f050      @ qdsub pc, r0, r1")
+       TEST_UNSUPPORTED(".word 0xe16f2050      @ qdsub r2, r0, pc")
+       TEST_UNSUPPORTED(".word 0xe161205f      @ qdsub r2, pc, r1")
+
+       TEST_UNSUPPORTED("bkpt  0xffff")
+       TEST_UNSUPPORTED("bkpt  0x0000")
+
+       TEST_UNSUPPORTED(".word 0xe1600070 @ smc #0")
+
+       TEST_GROUP("Halfword multiply and multiply-accumulate")
+
+       TEST_RRR(    "smlabb    r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "smlabbge  r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "smlabb    lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe10f3281 @ smlabb pc, r1, r2, r3")
+       TEST_RRR(    "smlatb    r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "smlatbge  r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "smlatb    lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe10f32a1 @ smlatb pc, r1, r2, r3")
+       TEST_RRR(    "smlabt    r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "smlabtge  r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "smlabt    lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe10f32c1 @ smlabt pc, r1, r2, r3")
+       TEST_RRR(    "smlatt    r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "smlattge  r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "smlatt    lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe10f32e1 @ smlatt pc, r1, r2, r3")
+
+       TEST_RRR(    "smlawb    r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "smlawbge  r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "smlawb    lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe12f3281 @ smlawb pc, r1, r2, r3")
+       TEST_RRR(    "smlawt    r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "smlawtge  r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "smlawt    lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe12f32c1 @ smlawt pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe12032cf @ smlawt r0, pc, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe1203fc1 @ smlawt r0, r1, pc, r3")
+       TEST_UNSUPPORTED(".word 0xe120f2c1 @ smlawt r0, r1, r2, pc")
+
+       TEST_RR(    "smulwb     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulwbge   r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_R(     "smulwb     lr, r",1, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe12f02a1 @ smulwb pc, r1, r2")
+       TEST_RR(    "smulwt     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulwtge   r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_R(     "smulwt     lr, r",1, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe12f02e1 @ smulwt pc, r1, r2")
+
+       TEST_RRRR(  "smlalbb    r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "smlalbble  r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "smlalbb    r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe14f1382 @ smlalbb pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe141f382 @ smlalbb r1, pc, r2, r3")
+       TEST_RRRR(  "smlaltb    r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "smlaltble  r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "smlaltb    r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe14f13a2 @ smlaltb pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe141f3a2 @ smlaltb r1, pc, r2, r3")
+       TEST_RRRR(  "smlalbt    r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "smlalbtle  r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "smlalbt    r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe14f13c2 @ smlalbt pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe141f3c2 @ smlalbt r1, pc, r2, r3")
+       TEST_RRRR(  "smlaltt    r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "smlalttle  r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "smlaltt    r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe14f13e2 @ smlalbb pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe140f3e2 @ smlalbb r0, pc, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe14013ef @ smlalbb r0, r1, pc, r3")
+       TEST_UNSUPPORTED(".word 0xe1401fe2 @ smlalbb r0, r1, r2, pc")
+
+       TEST_RR(    "smulbb     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulbbge   r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_R(     "smulbb     lr, r",1, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe16f0281 @ smulbb pc, r1, r2")
+       TEST_RR(    "smultb     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smultbge   r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_R(     "smultb     lr, r",1, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe16f02a1 @ smultb pc, r1, r2")
+       TEST_RR(    "smulbt     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulbtge   r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_R(     "smulbt     lr, r",1, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe16f02c1 @ smultb pc, r1, r2")
+       TEST_RR(    "smultt     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulttge   r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_R(     "smultt     lr, r",1, VAL2,", r13")
+       TEST_UNSUPPORTED(".word 0xe16f02e1 @ smultt pc, r1, r2")
+       TEST_UNSUPPORTED(".word 0xe16002ef @ smultt r0, pc, r2")
+       TEST_UNSUPPORTED(".word 0xe1600fe1 @ smultt r0, r1, pc")
+
+       TEST_GROUP("Multiply and multiply-accumulate")
+
+       TEST_RR(    "mul        r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "mulls      r7, r",8, VAL2,", r",9, VAL2,"")
+       TEST_R(     "mul        lr, r",4, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe00f0291 @ mul pc, r1, r2")
+       TEST_UNSUPPORTED(".word 0xe000029f @ mul r0, pc, r2")
+       TEST_UNSUPPORTED(".word 0xe0000f91 @ mul r0, r1, pc")
+       TEST_RR(    "muls       r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "mullss     r7, r",8, VAL2,", r",9, VAL2,"")
+       TEST_R(     "muls       lr, r",4, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe01f0291 @ muls pc, r1, r2")
+
+       TEST_RRR(    "mla       r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "mlahi     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "mla       lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe02f3291 @ mla pc, r1, r2, r3")
+       TEST_RRR(    "mlas      r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(    "mlahis    r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(     "mlas      lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe03f3291 @ mlas pc, r1, r2, r3")
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_RR(  "umaal        r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(  "umaalls      r7, r8, r",9, VAL2,", r",10, VAL1,"")
+       TEST_R(   "umaal        lr, r12, r",11,VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe041f392 @ umaal pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe04f0392 @ umaal r0, pc, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0500090 @ undef")
+       TEST_UNSUPPORTED(".word 0xe05fff9f @ undef")
+
+       TEST_RRR(  "mls         r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(  "mlshi       r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(   "mls         lr, r",1, VAL2,", r",2, VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe06f3291 @ mls pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe060329f @ mls r0, pc, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0603f91 @ mls r0, r1, pc, r3")
+       TEST_UNSUPPORTED(".word 0xe060f291 @ mls r0, r1, r2, pc")
+#endif
+
+       TEST_UNSUPPORTED(".word 0xe0700090 @ undef")
+       TEST_UNSUPPORTED(".word 0xe07fff9f @ undef")
+
+       TEST_RR(  "umull        r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(  "umullls      r7, r8, r",9, VAL2,", r",10, VAL1,"")
+       TEST_R(   "umull        lr, r12, r",11,VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe081f392 @ umull pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe08f1392 @ umull r1, pc, r2, r3")
+       TEST_RR(  "umulls       r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(  "umulllss     r7, r8, r",9, VAL2,", r",10, VAL1,"")
+       TEST_R(   "umulls       lr, r12, r",11,VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe091f392 @ umulls pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe09f1392 @ umulls r1, pc, r2, r3")
+
+       TEST_RRRR(  "umlal      r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "umlalle    r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "umlal      r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe0af1392 @ umlal pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0a1f392 @ umlal r1, pc, r2, r3")
+       TEST_RRRR(  "umlals     r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "umlalles   r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "umlals     r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe0bf1392 @ umlals pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0b1f392 @ umlals r1, pc, r2, r3")
+
+       TEST_RR(  "smull        r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(  "smullls      r7, r8, r",9, VAL2,", r",10, VAL1,"")
+       TEST_R(   "smull        lr, r12, r",11,VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe0c1f392 @ smull pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0cf1392 @ smull r1, pc, r2, r3")
+       TEST_RR(  "smulls       r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(  "smulllss     r7, r8, r",9, VAL2,", r",10, VAL1,"")
+       TEST_R(   "smulls       lr, r12, r",11,VAL3,", r13")
+       TEST_UNSUPPORTED(".word 0xe0d1f392 @ smulls pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0df1392 @ smulls r1, pc, r2, r3")
+
+       TEST_RRRR(  "smlal      r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "smlalle    r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "smlal      r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe0ef1392 @ smlal pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0e1f392 @ smlal r1, pc, r2, r3")
+       TEST_RRRR(  "smlals     r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR(  "smlalles   r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRR(   "smlals     r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13")
+       TEST_UNSUPPORTED(".word 0xe0ff1392 @ smlals pc, r1, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0f0f392 @ smlals r0, pc, r2, r3")
+       TEST_UNSUPPORTED(".word 0xe0f0139f @ smlals r0, r1, pc, r3")
+       TEST_UNSUPPORTED(".word 0xe0f01f92 @ smlals r0, r1, r2, pc")
+
+       TEST_GROUP("Synchronization primitives")
+
+       /*
+        * Use hard coded constants for SWP instructions to avoid warnings
+        * about deprecated instructions.
+        */
+       TEST_RP( ".word 0xe108e097 @ swp        lr, r",7,VAL2,", [r",8,0,"]")
+       TEST_R(  ".word 0x610d0091 @ swpvs      r0, r",1,VAL1,", [sp]")
+       TEST_RP( ".word 0xe10cd09e @ swp        sp, r",14,VAL2,", [r",12,13*4,"]")
+       TEST_UNSUPPORTED(".word 0xe102f091 @ swp pc, r1, [r2]")
+       TEST_UNSUPPORTED(".word 0xe102009f @ swp r0, pc, [r2]")
+       TEST_UNSUPPORTED(".word 0xe10f0091 @ swp r0, r1, [pc]")
+       TEST_RP( ".word 0xe148e097 @ swpb       lr, r",7,VAL2,", [r",8,0,"]")
+       TEST_R(  ".word 0x614d0091 @ swpvsb     r0, r",1,VAL1,", [sp]")
+       TEST_UNSUPPORTED(".word 0xe142f091 @ swpb pc, r1, [r2]")
+
+       TEST_UNSUPPORTED(".word 0xe1100090") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe1200090") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe1300090") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe1500090") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe1600090") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe1700090") /* Unallocated space */
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_UNSUPPORTED("ldrex r2, [sp]")
+       TEST_UNSUPPORTED("strexd        r0, r2, r3, [sp]")
+       TEST_UNSUPPORTED("ldrexd        r2, r3, [sp]")
+       TEST_UNSUPPORTED("strexb        r0, r2, [sp]")
+       TEST_UNSUPPORTED("ldrexb        r2, [sp]")
+       TEST_UNSUPPORTED("strexh        r0, r2, [sp]")
+       TEST_UNSUPPORTED("ldrexh        r2, [sp]")
+#endif
+       TEST_GROUP("Extra load/store instructions")
+
+       TEST_RPR(  "strh        r",0, VAL1,", [r",1, 48,", -r",2, 24,"]")
+       TEST_RPR(  "streqh      r",14,VAL2,", [r",13,0, ", r",12, 48,"]")
+       TEST_RPR(  "strh        r",1, VAL1,", [r",2, 24,", r",3,  48,"]!")
+       TEST_RPR(  "strneh      r",12,VAL2,", [r",11,48,", -r",10,24,"]!")
+       TEST_RPR(  "strh        r",2, VAL1,", [r",3, 24,"], r",4, 48,"")
+       TEST_RPR(  "strh        r",10,VAL2,", [r",9, 48,"], -r",11,24,"")
+       TEST_UNSUPPORTED(".word 0xe1afc0ba      @ strh r12, [pc, r10]!")
+       TEST_UNSUPPORTED(".word 0xe089f0bb      @ strh pc, [r9], r11")
+       TEST_UNSUPPORTED(".word 0xe089a0bf      @ strh r10, [r9], pc")
+
+       TEST_PR(   "ldrh        r0, [r",0,  48,", -r",2, 24,"]")
+       TEST_PR(   "ldrcsh      r14, [r",13,0, ", r",12, 48,"]")
+       TEST_PR(   "ldrh        r1, [r",2,  24,", r",3,  48,"]!")
+       TEST_PR(   "ldrcch      r12, [r",11,48,", -r",10,24,"]!")
+       TEST_PR(   "ldrh        r2, [r",3,  24,"], r",4, 48,"")
+       TEST_PR(   "ldrh        r10, [r",9, 48,"], -r",11,24,"")
+       TEST_UNSUPPORTED(".word 0xe1bfc0ba      @ ldrh r12, [pc, r10]!")
+       TEST_UNSUPPORTED(".word 0xe099f0bb      @ ldrh pc, [r9], r11")
+       TEST_UNSUPPORTED(".word 0xe099a0bf      @ ldrh r10, [r9], pc")
+
+       TEST_RP(   "strh        r",0, VAL1,", [r",1, 24,", #-2]")
+       TEST_RP(   "strmih      r",14,VAL2,", [r",13,0, ", #2]")
+       TEST_RP(   "strh        r",1, VAL1,", [r",2, 24,", #4]!")
+       TEST_RP(   "strplh      r",12,VAL2,", [r",11,24,", #-4]!")
+       TEST_RP(   "strh        r",2, VAL1,", [r",3, 24,"], #48")
+       TEST_RP(   "strh        r",10,VAL2,", [r",9, 64,"], #-48")
+       TEST_UNSUPPORTED(".word 0xe1efc3b0      @ strh r12, [pc, #48]!")
+       TEST_UNSUPPORTED(".word 0xe0c9f3b0      @ strh pc, [r9], #48")
+
+       TEST_P(    "ldrh        r0, [r",0,  24,", #-2]")
+       TEST_P(    "ldrvsh      r14, [r",13,0, ", #2]")
+       TEST_P(    "ldrh        r1, [r",2,  24,", #4]!")
+       TEST_P(    "ldrvch      r12, [r",11,24,", #-4]!")
+       TEST_P(    "ldrh        r2, [r",3,  24,"], #48")
+       TEST_P(    "ldrh        r10, [r",9, 64,"], #-48")
+       TEST(      "ldrh        r0, [pc, #0]")
+       TEST_UNSUPPORTED(".word 0xe1ffc3b0      @ ldrh r12, [pc, #48]!")
+       TEST_UNSUPPORTED(".word 0xe0d9f3b0      @ ldrh pc, [r9], #48")
+
+       TEST_PR(   "ldrsb       r0, [r",0,  48,", -r",2, 24,"]")
+       TEST_PR(   "ldrhisb     r14, [r",13,0,", r",12,  48,"]")
+       TEST_PR(   "ldrsb       r1, [r",2,  24,", r",3,  48,"]!")
+       TEST_PR(   "ldrlssb     r12, [r",11,48,", -r",10,24,"]!")
+       TEST_PR(   "ldrsb       r2, [r",3,  24,"], r",4, 48,"")
+       TEST_PR(   "ldrsb       r10, [r",9, 48,"], -r",11,24,"")
+       TEST_UNSUPPORTED(".word 0xe1bfc0da      @ ldrsb r12, [pc, r10]!")
+       TEST_UNSUPPORTED(".word 0xe099f0db      @ ldrsb pc, [r9], r11")
+
+       TEST_P(    "ldrsb       r0, [r",0,  24,", #-1]")
+       TEST_P(    "ldrgesb     r14, [r",13,0, ", #1]")
+       TEST_P(    "ldrsb       r1, [r",2,  24,", #4]!")
+       TEST_P(    "ldrltsb     r12, [r",11,24,", #-4]!")
+       TEST_P(    "ldrsb       r2, [r",3,  24,"], #48")
+       TEST_P(    "ldrsb       r10, [r",9, 64,"], #-48")
+       TEST(      "ldrsb       r0, [pc, #0]")
+       TEST_UNSUPPORTED(".word 0xe1ffc3d0      @ ldrsb r12, [pc, #48]!")
+       TEST_UNSUPPORTED(".word 0xe0d9f3d0      @ ldrsb pc, [r9], #48")
+
+       TEST_PR(   "ldrsh       r0, [r",0,  48,", -r",2, 24,"]")
+       TEST_PR(   "ldrgtsh     r14, [r",13,0, ", r",12, 48,"]")
+       TEST_PR(   "ldrsh       r1, [r",2,  24,", r",3,  48,"]!")
+       TEST_PR(   "ldrlesh     r12, [r",11,48,", -r",10,24,"]!")
+       TEST_PR(   "ldrsh       r2, [r",3,  24,"], r",4, 48,"")
+       TEST_PR(   "ldrsh       r10, [r",9, 48,"], -r",11,24,"")
+       TEST_UNSUPPORTED(".word 0xe1bfc0fa      @ ldrsh r12, [pc, r10]!")
+       TEST_UNSUPPORTED(".word 0xe099f0fb      @ ldrsh pc, [r9], r11")
+
+       TEST_P(    "ldrsh       r0, [r",0,  24,", #-1]")
+       TEST_P(    "ldreqsh     r14, [r",13,0 ,", #1]")
+       TEST_P(    "ldrsh       r1, [r",2,  24,", #4]!")
+       TEST_P(    "ldrnesh     r12, [r",11,24,", #-4]!")
+       TEST_P(    "ldrsh       r2, [r",3,  24,"], #48")
+       TEST_P(    "ldrsh       r10, [r",9, 64,"], #-48")
+       TEST(      "ldrsh       r0, [pc, #0]")
+       TEST_UNSUPPORTED(".word 0xe1ffc3f0      @ ldrsh r12, [pc, #48]!")
+       TEST_UNSUPPORTED(".word 0xe0d9f3f0      @ ldrsh pc, [r9], #48")
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_UNSUPPORTED("strht r1, [r2], r3")
+       TEST_UNSUPPORTED("ldrht r1, [r2], r3")
+       TEST_UNSUPPORTED("strht r1, [r2], #48")
+       TEST_UNSUPPORTED("ldrht r1, [r2], #48")
+       TEST_UNSUPPORTED("ldrsbt        r1, [r2], r3")
+       TEST_UNSUPPORTED("ldrsbt        r1, [r2], #48")
+       TEST_UNSUPPORTED("ldrsht        r1, [r2], r3")
+       TEST_UNSUPPORTED("ldrsht        r1, [r2], #48")
+#endif
+
+       TEST_RPR(  "strd        r",0, VAL1,", [r",1, 48,", -r",2,24,"]")
+       TEST_RPR(  "strccd      r",8, VAL2,", [r",13,0, ", r",12,48,"]")
+       TEST_RPR(  "strd        r",4, VAL1,", [r",2, 24,", r",3, 48,"]!")
+       TEST_RPR(  "strcsd      r",12,VAL2,", [r",11,48,", -r",10,24,"]!")
+       TEST_RPR(  "strd        r",2, VAL1,", [r",3, 24,"], r",4,48,"")
+       TEST_RPR(  "strd        r",10,VAL2,", [r",9, 48,"], -r",7,24,"")
+       TEST_UNSUPPORTED(".word 0xe1afc0fa      @ strd r12, [pc, r10]!")
+
+       TEST_PR(   "ldrd        r0, [r",0, 48,", -r",2,24,"]")
+       TEST_PR(   "ldrmid      r8, [r",13,0, ", r",12,48,"]")
+       TEST_PR(   "ldrd        r4, [r",2, 24,", r",3, 48,"]!")
+       TEST_PR(   "ldrpld      r6, [r",11,48,", -r",10,24,"]!")
+       TEST_PR(   "ldrd        r2, [r",5, 24,"], r",4,48,"")
+       TEST_PR(   "ldrd        r10, [r",9,48,"], -r",7,24,"")
+       TEST_UNSUPPORTED(".word 0xe1afc0da      @ ldrd r12, [pc, r10]!")
+       TEST_UNSUPPORTED(".word 0xe089f0db      @ ldrd pc, [r9], r11")
+       TEST_UNSUPPORTED(".word 0xe089e0db      @ ldrd lr, [r9], r11")
+       TEST_UNSUPPORTED(".word 0xe089c0df      @ ldrd r12, [r9], pc")
+
+       TEST_RP(   "strd        r",0, VAL1,", [r",1, 24,", #-8]")
+       TEST_RP(   "strvsd      r",8, VAL2,", [r",13,0, ", #8]")
+       TEST_RP(   "strd        r",4, VAL1,", [r",2, 24,", #16]!")
+       TEST_RP(   "strvcd      r",12,VAL2,", [r",11,24,", #-16]!")
+       TEST_RP(   "strd        r",2, VAL1,", [r",4, 24,"], #48")
+       TEST_RP(   "strd        r",10,VAL2,", [r",9, 64,"], #-48")
+       TEST_UNSUPPORTED(".word 0xe1efc3f0      @ strd r12, [pc, #48]!")
+
+       TEST_P(    "ldrd        r0, [r",0, 24,", #-8]")
+       TEST_P(    "ldrhid      r8, [r",13,0, ", #8]")
+       TEST_P(    "ldrd        r4, [r",2, 24,", #16]!")
+       TEST_P(    "ldrlsd      r6, [r",11,24,", #-16]!")
+       TEST_P(    "ldrd        r2, [r",5, 24,"], #48")
+       TEST_P(    "ldrd        r10, [r",9,6,"], #-48")
+       TEST_UNSUPPORTED(".word 0xe1efc3d0      @ ldrd r12, [pc, #48]!")
+       TEST_UNSUPPORTED(".word 0xe0c9f3d0      @ ldrd pc, [r9], #48")
+       TEST_UNSUPPORTED(".word 0xe0c9e3d0      @ ldrd lr, [r9], #48")
+
+       TEST_GROUP("Miscellaneous")
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST("movw      r0, #0")
+       TEST("movw      r0, #0xffff")
+       TEST("movw      lr, #0xffff")
+       TEST_UNSUPPORTED(".word 0xe300f000      @ movw pc, #0")
+       TEST_R("movt    r",0, VAL1,", #0")
+       TEST_R("movt    r",0, VAL2,", #0xffff")
+       TEST_R("movt    r",14,VAL1,", #0xffff")
+       TEST_UNSUPPORTED(".word 0xe340f000      @ movt pc, #0")
+#endif
+
+       TEST_UNSUPPORTED("msr   cpsr, 0x13")
+       TEST_UNSUPPORTED("msr   cpsr_f, 0xf0000000")
+       TEST_UNSUPPORTED("msr   spsr, 0x13")
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_SUPPORTED("yield")
+       TEST("sev")
+       TEST("nop")
+       TEST("wfi")
+       TEST_SUPPORTED("wfe")
+       TEST_UNSUPPORTED("dbg #0")
+#endif
+
+       TEST_GROUP("Load/store word and unsigned byte")
+
+#define LOAD_STORE(byte)                                                       \
+       TEST_RP( "str"byte"     r",0, VAL1,", [r",1, 24,", #-2]")               \
+       TEST_RP( "str"byte"     r",14,VAL2,", [r",13,0, ", #2]")                \
+       TEST_RP( "str"byte"     r",1, VAL1,", [r",2, 24,", #4]!")               \
+       TEST_RP( "str"byte"     r",12,VAL2,", [r",11,24,", #-4]!")              \
+       TEST_RP( "str"byte"     r",2, VAL1,", [r",3, 24,"], #48")               \
+       TEST_RP( "str"byte"     r",10,VAL2,", [r",9, 64,"], #-48")              \
+       TEST_RPR("str"byte"     r",0, VAL1,", [r",1, 48,", -r",2, 24,"]")       \
+       TEST_RPR("str"byte"     r",14,VAL2,", [r",13,0, ", r",12, 48,"]")       \
+       TEST_RPR("str"byte"     r",1, VAL1,", [r",2, 24,", r",3,  48,"]!")      \
+       TEST_RPR("str"byte"     r",12,VAL2,", [r",11,48,", -r",10,24,"]!")      \
+       TEST_RPR("str"byte"     r",2, VAL1,", [r",3, 24,"], r",4, 48,"")        \
+       TEST_RPR("str"byte"     r",10,VAL2,", [r",9, 48,"], -r",11,24,"")       \
+       TEST_RPR("str"byte"     r",0, VAL1,", [r",1, 24,", r",2,  32,", asl #1]")\
+       TEST_RPR("str"byte"     r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\
+       TEST_RPR("str"byte"     r",1, VAL1,", [r",2, 24,", r",3,  32,", asr #3]!")\
+       TEST_RPR("str"byte"     r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\
+       TEST_P(  "ldr"byte"     r0, [r",0,  24,", #-2]")                        \
+       TEST_P(  "ldr"byte"     r14, [r",13,0, ", #2]")                         \
+       TEST_P(  "ldr"byte"     r1, [r",2,  24,", #4]!")                        \
+       TEST_P(  "ldr"byte"     r12, [r",11,24,", #-4]!")                       \
+       TEST_P(  "ldr"byte"     r2, [r",3,  24,"], #48")                        \
+       TEST_P(  "ldr"byte"     r10, [r",9, 64,"], #-48")                       \
+       TEST_PR( "ldr"byte"     r0, [r",0,  48,", -r",2, 24,"]")                \
+       TEST_PR( "ldr"byte"     r14, [r",13,0, ", r",12, 48,"]")                \
+       TEST_PR( "ldr"byte"     r1, [r",2,  24,", r",3, 48,"]!")                \
+       TEST_PR( "ldr"byte"     r12, [r",11,48,", -r",10,24,"]!")               \
+       TEST_PR( "ldr"byte"     r2, [r",3,  24,"], r",4, 48,"")                 \
+       TEST_PR( "ldr"byte"     r10, [r",9, 48,"], -r",11,24,"")                \
+       TEST_PR( "ldr"byte"     r0, [r",0,  24,", r",2,  32,", asl #1]")        \
+       TEST_PR( "ldr"byte"     r14, [r",13,0, ", r",12, 32,", lsr #2]")        \
+       TEST_PR( "ldr"byte"     r1, [r",2,  24,", r",3,  32,", asr #3]!")       \
+       TEST_PR( "ldr"byte"     r12, [r",11,24,", r",10, 4,", ror #31]!")       \
+       TEST(    "ldr"byte"     r0, [pc, #0]")                                  \
+       TEST_R(  "ldr"byte"     r12, [pc, r",14,0,"]")
+
+       LOAD_STORE("")
+       TEST_P(   "str  pc, [r",0,0,", #15*4]")
+       TEST_R(   "str  pc, [sp, r",2,15*4,"]")
+       TEST_BF(  "ldr  pc, [sp, #15*4]")
+       TEST_BF_R("ldr  pc, [sp, r",2,15*4,"]")
+
+       TEST_P(   "str  sp, [r",0,0,", #13*4]")
+       TEST_R(   "str  sp, [sp, r",2,13*4,"]")
+       TEST_BF(  "ldr  sp, [sp, #13*4]")
+       TEST_BF_R("ldr  sp, [sp, r",2,13*4,"]")
+
+#ifdef CONFIG_THUMB2_KERNEL
+       TEST_ARM_TO_THUMB_INTERWORK_P("ldr      pc, [r",0,0,", #15*4]")
+#endif
+       TEST_UNSUPPORTED(".word 0xe5af6008      @ str r6, [pc, #8]!")
+       TEST_UNSUPPORTED(".word 0xe7af6008      @ str r6, [pc, r8]!")
+       TEST_UNSUPPORTED(".word 0xe5bf6008      @ ldr r6, [pc, #8]!")
+       TEST_UNSUPPORTED(".word 0xe7bf6008      @ ldr r6, [pc, r8]!")
+       TEST_UNSUPPORTED(".word 0xe788600f      @ str r6, [r8, pc]")
+       TEST_UNSUPPORTED(".word 0xe798600f      @ ldr r6, [r8, pc]")
+
+       LOAD_STORE("b")
+       TEST_UNSUPPORTED(".word 0xe5f7f008      @ ldrb pc, [r7, #8]!")
+       TEST_UNSUPPORTED(".word 0xe7f7f008      @ ldrb pc, [r7, r8]!")
+       TEST_UNSUPPORTED(".word 0xe5ef6008      @ strb r6, [pc, #8]!")
+       TEST_UNSUPPORTED(".word 0xe7ef6008      @ strb r6, [pc, r3]!")
+       TEST_UNSUPPORTED(".word 0xe5ff6008      @ ldrb r6, [pc, #8]!")
+       TEST_UNSUPPORTED(".word 0xe7ff6008      @ ldrb r6, [pc, r3]!")
+
+       TEST_UNSUPPORTED("ldrt  r0, [r1], #4")
+       TEST_UNSUPPORTED("ldrt  r1, [r2], r3")
+       TEST_UNSUPPORTED("strt  r2, [r3], #4")
+       TEST_UNSUPPORTED("strt  r3, [r4], r5")
+       TEST_UNSUPPORTED("ldrbt r4, [r5], #4")
+       TEST_UNSUPPORTED("ldrbt r5, [r6], r7")
+       TEST_UNSUPPORTED("strbt r6, [r7], #4")
+       TEST_UNSUPPORTED("strbt r7, [r8], r9")
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_GROUP("Parallel addition and subtraction, signed")
+
+       TEST_UNSUPPORTED(".word 0xe6000010") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe60fffff") /* Unallocated space */
+
+       TEST_RR(    "sadd16     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sadd16     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe61cff1a      @ sadd16        pc, r12, r10")
+       TEST_RR(    "sasx       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sasx       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe61cff3a      @ sasx  pc, r12, r10")
+       TEST_RR(    "ssax       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "ssax       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe61cff5a      @ ssax  pc, r12, r10")
+       TEST_RR(    "ssub16     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "ssub16     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe61cff7a      @ ssub16        pc, r12, r10")
+       TEST_RR(    "sadd8      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sadd8      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe61cff9a      @ sadd8 pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe61000b0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe61fffbf") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe61000d0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe61fffdf") /* Unallocated space */
+       TEST_RR(    "ssub8      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "ssub8      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe61cfffa      @ ssub8 pc, r12, r10")
+
+       TEST_RR(    "qadd16     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "qadd16     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe62cff1a      @ qadd16        pc, r12, r10")
+       TEST_RR(    "qasx       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "qasx       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe62cff3a      @ qasx  pc, r12, r10")
+       TEST_RR(    "qsax       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "qsax       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe62cff5a      @ qsax  pc, r12, r10")
+       TEST_RR(    "qsub16     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "qsub16     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe62cff7a      @ qsub16        pc, r12, r10")
+       TEST_RR(    "qadd8      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "qadd8      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe62cff9a      @ qadd8 pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe62000b0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe62fffbf") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe62000d0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe62fffdf") /* Unallocated space */
+       TEST_RR(    "qsub8      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "qsub8      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe62cfffa      @ qsub8 pc, r12, r10")
+
+       TEST_RR(    "shadd16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "shadd16    r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe63cff1a      @ shadd16       pc, r12, r10")
+       TEST_RR(    "shasx      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "shasx      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe63cff3a      @ shasx pc, r12, r10")
+       TEST_RR(    "shsax      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "shsax      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe63cff5a      @ shsax pc, r12, r10")
+       TEST_RR(    "shsub16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "shsub16    r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe63cff7a      @ shsub16       pc, r12, r10")
+       TEST_RR(    "shadd8     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "shadd8     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe63cff9a      @ shadd8        pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe63000b0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe63fffbf") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe63000d0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe63fffdf") /* Unallocated space */
+       TEST_RR(    "shsub8     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "shsub8     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe63cfffa      @ shsub8        pc, r12, r10")
+
+       TEST_GROUP("Parallel addition and subtraction, unsigned")
+
+       TEST_UNSUPPORTED(".word 0xe6400010") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe64fffff") /* Unallocated space */
+
+       TEST_RR(    "uadd16     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uadd16     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe65cff1a      @ uadd16        pc, r12, r10")
+       TEST_RR(    "uasx       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uasx       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe65cff3a      @ uasx  pc, r12, r10")
+       TEST_RR(    "usax       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "usax       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe65cff5a      @ usax  pc, r12, r10")
+       TEST_RR(    "usub16     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "usub16     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe65cff7a      @ usub16        pc, r12, r10")
+       TEST_RR(    "uadd8      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uadd8      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe65cff9a      @ uadd8 pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe65000b0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe65fffbf") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe65000d0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe65fffdf") /* Unallocated space */
+       TEST_RR(    "usub8      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "usub8      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe65cfffa      @ usub8 pc, r12, r10")
+
+       TEST_RR(    "uqadd16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uqadd16    r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe66cff1a      @ uqadd16       pc, r12, r10")
+       TEST_RR(    "uqasx      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uqasx      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe66cff3a      @ uqasx pc, r12, r10")
+       TEST_RR(    "uqsax      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uqsax      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe66cff5a      @ uqsax pc, r12, r10")
+       TEST_RR(    "uqsub16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uqsub16    r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe66cff7a      @ uqsub16       pc, r12, r10")
+       TEST_RR(    "uqadd8     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uqadd8     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe66cff9a      @ uqadd8        pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe66000b0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe66fffbf") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe66000d0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe66fffdf") /* Unallocated space */
+       TEST_RR(    "uqsub8     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uqsub8     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe66cfffa      @ uqsub8        pc, r12, r10")
+
+       TEST_RR(    "uhadd16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uhadd16    r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe67cff1a      @ uhadd16       pc, r12, r10")
+       TEST_RR(    "uhasx      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uhasx      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe67cff3a      @ uhasx pc, r12, r10")
+       TEST_RR(    "uhsax      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uhsax      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe67cff5a      @ uhsax pc, r12, r10")
+       TEST_RR(    "uhsub16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uhsub16    r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe67cff7a      @ uhsub16       pc, r12, r10")
+       TEST_RR(    "uhadd8     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uhadd8     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe67cff9a      @ uhadd8        pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe67000b0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe67fffbf") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe67000d0") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe67fffdf") /* Unallocated space */
+       TEST_RR(    "uhsub8     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uhsub8     r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe67cfffa      @ uhsub8        pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe67feffa      @ uhsub8        r14, pc, r10")
+       TEST_UNSUPPORTED(".word 0xe67cefff      @ uhsub8        r14, r12, pc")
+#endif /* __LINUX_ARM_ARCH__ >= 7 */
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_GROUP("Packing, unpacking, saturation, and reversal")
+
+       TEST_RR(    "pkhbt      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "pkhbt      r14,r",12, HH1,", r",10,HH2,", lsl #2")
+       TEST_UNSUPPORTED(".word 0xe68cf11a      @ pkhbt pc, r12, r10, lsl #2")
+       TEST_RR(    "pkhtb      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "pkhtb      r14,r",12, HH1,", r",10,HH2,", asr #2")
+       TEST_UNSUPPORTED(".word 0xe68cf15a      @ pkhtb pc, r12, r10, asr #2")
+       TEST_UNSUPPORTED(".word 0xe68fe15a      @ pkhtb r14, pc, r10, asr #2")
+       TEST_UNSUPPORTED(".word 0xe68ce15f      @ pkhtb r14, r12, pc, asr #2")
+       TEST_UNSUPPORTED(".word 0xe6900010") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe69fffdf") /* Unallocated space */
+
+       TEST_R(     "ssat       r0, #24, r",0,   VAL1,"")
+       TEST_R(     "ssat       r14, #24, r",12, VAL2,"")
+       TEST_R(     "ssat       r0, #24, r",0,   VAL1,", lsl #8")
+       TEST_R(     "ssat       r14, #24, r",12, VAL2,", asr #8")
+       TEST_UNSUPPORTED(".word 0xe6b7f01c      @ ssat  pc, #24, r12")
+
+       TEST_R(     "usat       r0, #24, r",0,   VAL1,"")
+       TEST_R(     "usat       r14, #24, r",12, VAL2,"")
+       TEST_R(     "usat       r0, #24, r",0,   VAL1,", lsl #8")
+       TEST_R(     "usat       r14, #24, r",12, VAL2,", asr #8")
+       TEST_UNSUPPORTED(".word 0xe6f7f01c      @ usat  pc, #24, r12")
+
+       TEST_RR(    "sxtab16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sxtab16    r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "sxtb16     r8, r",7,  HH1,"")
+       TEST_UNSUPPORTED(".word 0xe68cf47a      @ sxtab16       pc,r12, r10, ror #8")
+
+       TEST_RR(    "sel        r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(    "sel        r14, r",12,VAL1,", r",10, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe68cffba      @ sel   pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe68fefba      @ sel   r14, pc, r10")
+       TEST_UNSUPPORTED(".word 0xe68cefbf      @ sel   r14, r12, pc")
+
+       TEST_R(     "ssat16     r0, #12, r",0,   HH1,"")
+       TEST_R(     "ssat16     r14, #12, r",12, HH2,"")
+       TEST_UNSUPPORTED(".word 0xe6abff3c      @ ssat16        pc, #12, r12")
+
+       TEST_RR(    "sxtab      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sxtab      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "sxtb       r8, r",7,  HH1,"")
+       TEST_UNSUPPORTED(".word 0xe6acf47a      @ sxtab pc,r12, r10, ror #8")
+
+       TEST_R(     "rev        r0, r",0,   VAL1,"")
+       TEST_R(     "rev        r14, r",12, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe6bfff3c      @ rev   pc, r12")
+
+       TEST_RR(    "sxtah      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sxtah      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "sxth       r8, r",7,  HH1,"")
+       TEST_UNSUPPORTED(".word 0xe6bcf47a      @ sxtah pc,r12, r10, ror #8")
+
+       TEST_R(     "rev16      r0, r",0,   VAL1,"")
+       TEST_R(     "rev16      r14, r",12, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe6bfffbc      @ rev16 pc, r12")
+
+       TEST_RR(    "uxtab16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uxtab16    r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "uxtb16     r8, r",7,  HH1,"")
+       TEST_UNSUPPORTED(".word 0xe6ccf47a      @ uxtab16       pc,r12, r10, ror #8")
+
+       TEST_R(     "usat16     r0, #12, r",0,   HH1,"")
+       TEST_R(     "usat16     r14, #12, r",12, HH2,"")
+       TEST_UNSUPPORTED(".word 0xe6ecff3c      @ usat16        pc, #12, r12")
+       TEST_UNSUPPORTED(".word 0xe6ecef3f      @ usat16        r14, #12, pc")
+
+       TEST_RR(    "uxtab      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uxtab      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "uxtb       r8, r",7,  HH1,"")
+       TEST_UNSUPPORTED(".word 0xe6ecf47a      @ uxtab pc,r12, r10, ror #8")
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_R(     "rbit       r0, r",0,   VAL1,"")
+       TEST_R(     "rbit       r14, r",12, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe6ffff3c      @ rbit  pc, r12")
+#endif
+
+       TEST_RR(    "uxtah      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uxtah      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "uxth       r8, r",7,  HH1,"")
+       TEST_UNSUPPORTED(".word 0xe6fff077      @ uxth  pc, r7")
+       TEST_UNSUPPORTED(".word 0xe6ff807f      @ uxth  r8, pc")
+       TEST_UNSUPPORTED(".word 0xe6fcf47a      @ uxtah pc, r12, r10, ror #8")
+       TEST_UNSUPPORTED(".word 0xe6fce47f      @ uxtah r14, r12, pc, ror #8")
+
+       TEST_R(     "revsh      r0, r",0,   VAL1,"")
+       TEST_R(     "revsh      r14, r",12, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe6ffff3c      @ revsh pc, r12")
+       TEST_UNSUPPORTED(".word 0xe6ffef3f      @ revsh r14, pc")
+
+       TEST_UNSUPPORTED(".word 0xe6900070") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe69fff7f") /* Unallocated space */
+
+       TEST_UNSUPPORTED(".word 0xe6d00070") /* Unallocated space */
+       TEST_UNSUPPORTED(".word 0xe6dfff7f") /* Unallocated space */
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_GROUP("Signed multiplies")
+
+       TEST_RRR(   "smlad      r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smlad      r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe70f8a1c      @ smlad pc, r12, r10, r8")
+       TEST_RRR(   "smladx     r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smladx     r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe70f8a3c      @ smladx        pc, r12, r10, r8")
+
+       TEST_RR(   "smuad       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(   "smuad       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe70ffa1c      @ smuad pc, r12, r10")
+       TEST_RR(   "smuadx      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(   "smuadx      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe70ffa3c      @ smuadx        pc, r12, r10")
+
+       TEST_RRR(   "smlsd      r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smlsd      r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe70f8a5c      @ smlsd pc, r12, r10, r8")
+       TEST_RRR(   "smlsdx     r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smlsdx     r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe70f8a7c      @ smlsdx        pc, r12, r10, r8")
+
+       TEST_RR(   "smusd       r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(   "smusd       r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe70ffa5c      @ smusd pc, r12, r10")
+       TEST_RR(   "smusdx      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(   "smusdx      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_UNSUPPORTED(".word 0xe70ffa7c      @ smusdx        pc, r12, r10")
+
+       TEST_RRRR( "smlald      r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2)
+       TEST_RRRR( "smlald      r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1)
+       TEST_UNSUPPORTED(".word 0xe74af819      @ smlald        pc, r10, r9, r8")
+       TEST_UNSUPPORTED(".word 0xe74fb819      @ smlald        r11, pc, r9, r8")
+       TEST_UNSUPPORTED(".word 0xe74ab81f      @ smlald        r11, r10, pc, r8")
+       TEST_UNSUPPORTED(".word 0xe74abf19      @ smlald        r11, r10, r9, pc")
+
+       TEST_RRRR( "smlaldx     r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2)
+       TEST_RRRR( "smlaldx     r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1)
+       TEST_UNSUPPORTED(".word 0xe74af839      @ smlaldx       pc, r10, r9, r8")
+       TEST_UNSUPPORTED(".word 0xe74fb839      @ smlaldx       r11, pc, r9, r8")
+
+       TEST_RRR(  "smmla       r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(  "smmla       r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe75f8a1c      @ smmla pc, r12, r10, r8")
+       TEST_RRR(  "smmlar      r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(  "smmlar      r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe75f8a3c      @ smmlar        pc, r12, r10, r8")
+
+       TEST_RR(   "smmul       r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(   "smmul       r14, r",12,VAL2,", r",10,VAL1,"")
+       TEST_UNSUPPORTED(".word 0xe75ffa1c      @ smmul pc, r12, r10")
+       TEST_RR(   "smmulr      r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(   "smmulr      r14, r",12,VAL2,", r",10,VAL1,"")
+       TEST_UNSUPPORTED(".word 0xe75ffa3c      @ smmulr        pc, r12, r10")
+
+       TEST_RRR(  "smmls       r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(  "smmls       r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe75f8adc      @ smmls pc, r12, r10, r8")
+       TEST_RRR(  "smmlsr      r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(  "smmlsr      r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_UNSUPPORTED(".word 0xe75f8afc      @ smmlsr        pc, r12, r10, r8")
+       TEST_UNSUPPORTED(".word 0xe75e8aff      @ smmlsr        r14, pc, r10, r8")
+       TEST_UNSUPPORTED(".word 0xe75e8ffc      @ smmlsr        r14, r12, pc, r8")
+       TEST_UNSUPPORTED(".word 0xe75efafc      @ smmlsr        r14, r12, r10, pc")
+
+       TEST_RR(   "usad8       r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(   "usad8       r14, r",12,VAL2,", r",10,VAL1,"")
+       TEST_UNSUPPORTED(".word 0xe75ffa1c      @ usad8 pc, r12, r10")
+       TEST_UNSUPPORTED(".word 0xe75efa1f      @ usad8 r14, pc, r10")
+       TEST_UNSUPPORTED(".word 0xe75eff1c      @ usad8 r14, r12, pc")
+
+       TEST_RRR(  "usada8      r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL3,"")
+       TEST_RRR(  "usada8      r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"")
+       TEST_UNSUPPORTED(".word 0xe78f8a1c      @ usada8        pc, r12, r10, r8")
+       TEST_UNSUPPORTED(".word 0xe78e8a1f      @ usada8        r14, pc, r10, r8")
+       TEST_UNSUPPORTED(".word 0xe78e8f1c      @ usada8        r14, r12, pc, r8")
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_GROUP("Bit Field")
+
+       TEST_R(     "sbfx       r0, r",0  , VAL1,", #0, #31")
+       TEST_R(     "sbfxeq     r14, r",12, VAL2,", #8, #16")
+       TEST_R(     "sbfx       r4, r",10,  VAL1,", #16, #15")
+       TEST_UNSUPPORTED(".word 0xe7aff45c      @ sbfx  pc, r12, #8, #16")
+
+       TEST_R(     "ubfx       r0, r",0  , VAL1,", #0, #31")
+       TEST_R(     "ubfxcs     r14, r",12, VAL2,", #8, #16")
+       TEST_R(     "ubfx       r4, r",10,  VAL1,", #16, #15")
+       TEST_UNSUPPORTED(".word 0xe7eff45c      @ ubfx  pc, r12, #8, #16")
+       TEST_UNSUPPORTED(".word 0xe7efc45f      @ ubfx  r12, pc, #8, #16")
+
+       TEST_R(     "bfc        r",0, VAL1,", #4, #20")
+       TEST_R(     "bfcvs      r",14,VAL2,", #4, #20")
+       TEST_R(     "bfc        r",7, VAL1,", #0, #31")
+       TEST_R(     "bfc        r",8, VAL2,", #0, #31")
+       TEST_UNSUPPORTED(".word 0xe7def01f      @ bfc   pc, #0, #31");
+
+       TEST_RR(    "bfi        r",0, VAL1,", r",0  , VAL2,", #0, #31")
+       TEST_RR(    "bfipl      r",12,VAL1,", r",14 , VAL2,", #4, #20")
+       TEST_UNSUPPORTED(".word 0xe7d7f21e      @ bfi   pc, r14, #4, #20")
+
+       TEST_UNSUPPORTED(".word 0x07f000f0")  /* Permanently UNDEFINED */
+       TEST_UNSUPPORTED(".word 0x07ffffff")  /* Permanently UNDEFINED */
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
+       TEST_GROUP("Branch, branch with link, and block data transfer")
+
+       TEST_P(   "stmda        r",0, 16*4,", {r0}")
+       TEST_P(   "stmeqda      r",4, 16*4,", {r0-r15}")
+       TEST_P(   "stmneda      r",8, 16*4,"!, {r8-r15}")
+       TEST_P(   "stmda        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_P(   "stmda        r",13,0,   "!, {pc}")
+
+       TEST_P(   "ldmda        r",0, 16*4,", {r0}")
+       TEST_BF_P("ldmcsda      r",4, 15*4,", {r0-r15}")
+       TEST_BF_P("ldmccda      r",7, 15*4,"!, {r8-r15}")
+       TEST_P(   "ldmda        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_BF_P("ldmda        r",14,15*4,"!, {pc}")
+
+       TEST_P(   "stmia        r",0, 16*4,", {r0}")
+       TEST_P(   "stmmiia      r",4, 16*4,", {r0-r15}")
+       TEST_P(   "stmplia      r",8, 16*4,"!, {r8-r15}")
+       TEST_P(   "stmia        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_P(   "stmia        r",14,0,   "!, {pc}")
+
+       TEST_P(   "ldmia        r",0, 16*4,", {r0}")
+       TEST_BF_P("ldmvsia      r",4, 0,   ", {r0-r15}")
+       TEST_BF_P("ldmvcia      r",7, 8*4, "!, {r8-r15}")
+       TEST_P(   "ldmia        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_BF_P("ldmia        r",14,15*4,"!, {pc}")
+
+       TEST_P(   "stmdb        r",0, 16*4,", {r0}")
+       TEST_P(   "stmhidb      r",4, 16*4,", {r0-r15}")
+       TEST_P(   "stmlsdb      r",8, 16*4,"!, {r8-r15}")
+       TEST_P(   "stmdb        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_P(   "stmdb        r",13,4,   "!, {pc}")
+
+       TEST_P(   "ldmdb        r",0, 16*4,", {r0}")
+       TEST_BF_P("ldmgedb      r",4, 16*4,", {r0-r15}")
+       TEST_BF_P("ldmltdb      r",7, 16*4,"!, {r8-r15}")
+       TEST_P(   "ldmdb        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_BF_P("ldmdb        r",14,16*4,"!, {pc}")
+
+       TEST_P(   "stmib        r",0, 16*4,", {r0}")
+       TEST_P(   "stmgtib      r",4, 16*4,", {r0-r15}")
+       TEST_P(   "stmleib      r",8, 16*4,"!, {r8-r15}")
+       TEST_P(   "stmib        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_P(   "stmib        r",13,-4,  "!, {pc}")
+
+       TEST_P(   "ldmib        r",0, 16*4,", {r0}")
+       TEST_BF_P("ldmeqib      r",4, -4,", {r0-r15}")
+       TEST_BF_P("ldmneib      r",7, 7*4,"!, {r8-r15}")
+       TEST_P(   "ldmib        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_BF_P("ldmib        r",14,14*4,"!, {pc}")
+
+       TEST_P(   "stmdb        r",13,16*4,"!, {r3-r12,lr}")
+       TEST_P(   "stmeqdb      r",13,16*4,"!, {r3-r12}")
+       TEST_P(   "stmnedb      r",2, 16*4,", {r3-r12,lr}")
+       TEST_P(   "stmdb        r",13,16*4,"!, {r2-r12,lr}")
+       TEST_P(   "stmdb        r",0, 16*4,", {r0-r12}")
+       TEST_P(   "stmdb        r",0, 16*4,", {r0-r12,lr}")
+
+       TEST_BF_P("ldmia        r",13,5*4, "!, {r3-r12,pc}")
+       TEST_P(   "ldmccia      r",13,5*4, "!, {r3-r12}")
+       TEST_BF_P("ldmcsia      r",2, 5*4, "!, {r3-r12,pc}")
+       TEST_BF_P("ldmia        r",13,4*4, "!, {r2-r12,pc}")
+       TEST_P(   "ldmia        r",0, 16*4,", {r0-r12}")
+       TEST_P(   "ldmia        r",0, 16*4,", {r0-r12,lr}")
+
+#ifdef CONFIG_THUMB2_KERNEL
+       TEST_ARM_TO_THUMB_INTERWORK_P("ldmplia  r",0,15*4,", {pc}")
+       TEST_ARM_TO_THUMB_INTERWORK_P("ldmmiia  r",13,0,", {r0-r15}")
+#endif
+       TEST_BF("b      2f")
+       TEST_BF("bl     2f")
+       TEST_BB("b      2b")
+       TEST_BB("bl     2b")
+
+       TEST_BF("beq    2f")
+       TEST_BF("bleq   2f")
+       TEST_BB("bne    2b")
+       TEST_BB("blne   2b")
+
+       TEST_BF("bgt    2f")
+       TEST_BF("blgt   2f")
+       TEST_BB("blt    2b")
+       TEST_BB("bllt   2b")
+
+       TEST_GROUP("Supervisor Call, and coprocessor instructions")
+
+       /*
+        * We can't really test these by executing them, so all
+        * we can do is check that probes are, or are not allowed.
+        * At the moment none are allowed...
+        */
+#define TEST_COPROCESSOR(code) TEST_UNSUPPORTED(code)
+
+#define COPROCESSOR_INSTRUCTIONS_ST_LD(two,cc)                                 \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13, #4]")                     \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13, #-4]")                    \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13, #4]!")                    \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13, #-4]!")                   \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13], #4")                     \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13], #-4")                    \
+       TEST_COPROCESSOR("stc"two"      0, cr0, [r13], {1}")                    \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13, #4]")                     \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13, #-4]")                    \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13, #4]!")                    \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13, #-4]!")                   \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13], #4")                     \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13], #-4")                    \
+       TEST_COPROCESSOR("stc"two"l     0, cr0, [r13], {1}")                    \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13, #4]")                     \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13, #-4]")                    \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13, #4]!")                    \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13, #-4]!")                   \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13], #4")                     \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13], #-4")                    \
+       TEST_COPROCESSOR("ldc"two"      0, cr0, [r13], {1}")                    \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13, #4]")                     \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13, #-4]")                    \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13, #4]!")                    \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13, #-4]!")                   \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13], #4")                     \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13], #-4")                    \
+       TEST_COPROCESSOR("ldc"two"l     0, cr0, [r13], {1}")                    \
+                                                                               \
+       TEST_COPROCESSOR( "stc"two"     0, cr0, [r15, #4]")                     \
+       TEST_COPROCESSOR( "stc"two"     0, cr0, [r15, #-4]")                    \
+       TEST_UNSUPPORTED(".word 0x"cc"daf0001   @ stc"two"      0, cr0, [r15, #4]!")    \
+       TEST_UNSUPPORTED(".word 0x"cc"d2f0001   @ stc"two"      0, cr0, [r15, #-4]!")   \
+       TEST_UNSUPPORTED(".word 0x"cc"caf0001   @ stc"two"      0, cr0, [r15], #4")     \
+       TEST_UNSUPPORTED(".word 0x"cc"c2f0001   @ stc"two"      0, cr0, [r15], #-4")    \
+       TEST_COPROCESSOR( "stc"two"     0, cr0, [r15], {1}")                    \
+       TEST_COPROCESSOR( "stc"two"l    0, cr0, [r15, #4]")                     \
+       TEST_COPROCESSOR( "stc"two"l    0, cr0, [r15, #-4]")                    \
+       TEST_UNSUPPORTED(".word 0x"cc"def0001   @ stc"two"l     0, cr0, [r15, #4]!")    \
+       TEST_UNSUPPORTED(".word 0x"cc"d6f0001   @ stc"two"l     0, cr0, [r15, #-4]!")   \
+       TEST_UNSUPPORTED(".word 0x"cc"cef0001   @ stc"two"l     0, cr0, [r15], #4")     \
+       TEST_UNSUPPORTED(".word 0x"cc"c6f0001   @ stc"two"l     0, cr0, [r15], #-4")    \
+       TEST_COPROCESSOR( "stc"two"l    0, cr0, [r15], {1}")                    \
+       TEST_COPROCESSOR( "ldc"two"     0, cr0, [r15, #4]")                     \
+       TEST_COPROCESSOR( "ldc"two"     0, cr0, [r15, #-4]")                    \
+       TEST_UNSUPPORTED(".word 0x"cc"dbf0001   @ ldc"two"      0, cr0, [r15, #4]!")    \
+       TEST_UNSUPPORTED(".word 0x"cc"d3f0001   @ ldc"two"      0, cr0, [r15, #-4]!")   \
+       TEST_UNSUPPORTED(".word 0x"cc"cbf0001   @ ldc"two"      0, cr0, [r15], #4")     \
+       TEST_UNSUPPORTED(".word 0x"cc"c3f0001   @ ldc"two"      0, cr0, [r15], #-4")    \
+       TEST_COPROCESSOR( "ldc"two"     0, cr0, [r15], {1}")                    \
+       TEST_COPROCESSOR( "ldc"two"l    0, cr0, [r15, #4]")                     \
+       TEST_COPROCESSOR( "ldc"two"l    0, cr0, [r15, #-4]")                    \
+       TEST_UNSUPPORTED(".word 0x"cc"dff0001   @ ldc"two"l     0, cr0, [r15, #4]!")    \
+       TEST_UNSUPPORTED(".word 0x"cc"d7f0001   @ ldc"two"l     0, cr0, [r15, #-4]!")   \
+       TEST_UNSUPPORTED(".word 0x"cc"cff0001   @ ldc"two"l     0, cr0, [r15], #4")     \
+       TEST_UNSUPPORTED(".word 0x"cc"c7f0001   @ ldc"two"l     0, cr0, [r15], #-4")    \
+       TEST_COPROCESSOR( "ldc"two"l    0, cr0, [r15], {1}")
+
+#define COPROCESSOR_INSTRUCTIONS_MC_MR(two,cc)                                 \
+                                                                               \
+       TEST_COPROCESSOR( "mcrr"two"    0, 15, r0, r14, cr0")                   \
+       TEST_COPROCESSOR( "mcrr"two"    15, 0, r14, r0, cr15")                  \
+       TEST_UNSUPPORTED(".word 0x"cc"c4f00f0   @ mcrr"two"     0, 15, r0, r15, cr0")   \
+       TEST_UNSUPPORTED(".word 0x"cc"c40ff0f   @ mcrr"two"     15, 0, r15, r0, cr15")  \
+       TEST_COPROCESSOR( "mrrc"two"    0, 15, r0, r14, cr0")                   \
+       TEST_COPROCESSOR( "mrrc"two"    15, 0, r14, r0, cr15")                  \
+       TEST_UNSUPPORTED(".word 0x"cc"c5f00f0   @ mrrc"two"     0, 15, r0, r15, cr0")   \
+       TEST_UNSUPPORTED(".word 0x"cc"c50ff0f   @ mrrc"two"     15, 0, r15, r0, cr15")  \
+       TEST_COPROCESSOR( "cdp"two"     15, 15, cr15, cr15, cr15, 7")           \
+       TEST_COPROCESSOR( "cdp"two"     0, 0, cr0, cr0, cr0, 0")                \
+       TEST_COPROCESSOR( "mcr"two"     15, 7, r15, cr15, cr15, 7")             \
+       TEST_COPROCESSOR( "mcr"two"     0, 0, r0, cr0, cr0, 0")                 \
+       TEST_COPROCESSOR( "mrc"two"     15, 7, r15, cr15, cr15, 7")             \
+       TEST_COPROCESSOR( "mrc"two"     0, 0, r0, cr0, cr0, 0")
+
+       COPROCESSOR_INSTRUCTIONS_ST_LD("","e")
+       COPROCESSOR_INSTRUCTIONS_MC_MR("","e")
+       TEST_UNSUPPORTED("svc   0")
+       TEST_UNSUPPORTED("svc   0xffffff")
+
+       TEST_UNSUPPORTED("svc   0")
+
+       TEST_GROUP("Unconditional instruction")
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_UNSUPPORTED("srsda sp, 0x13")
+       TEST_UNSUPPORTED("srsdb sp, 0x13")
+       TEST_UNSUPPORTED("srsia sp, 0x13")
+       TEST_UNSUPPORTED("srsib sp, 0x13")
+       TEST_UNSUPPORTED("srsda sp!, 0x13")
+       TEST_UNSUPPORTED("srsdb sp!, 0x13")
+       TEST_UNSUPPORTED("srsia sp!, 0x13")
+       TEST_UNSUPPORTED("srsib sp!, 0x13")
+
+       TEST_UNSUPPORTED("rfeda sp")
+       TEST_UNSUPPORTED("rfedb sp")
+       TEST_UNSUPPORTED("rfeia sp")
+       TEST_UNSUPPORTED("rfeib sp")
+       TEST_UNSUPPORTED("rfeda sp!")
+       TEST_UNSUPPORTED("rfedb sp!")
+       TEST_UNSUPPORTED("rfeia sp!")
+       TEST_UNSUPPORTED("rfeib sp!")
+       TEST_UNSUPPORTED(".word 0xf81d0a00      @ rfeda pc")
+       TEST_UNSUPPORTED(".word 0xf91d0a00      @ rfedb pc")
+       TEST_UNSUPPORTED(".word 0xf89d0a00      @ rfeia pc")
+       TEST_UNSUPPORTED(".word 0xf99d0a00      @ rfeib pc")
+       TEST_UNSUPPORTED(".word 0xf83d0a00      @ rfeda pc!")
+       TEST_UNSUPPORTED(".word 0xf93d0a00      @ rfedb pc!")
+       TEST_UNSUPPORTED(".word 0xf8bd0a00      @ rfeia pc!")
+       TEST_UNSUPPORTED(".word 0xf9bd0a00      @ rfeib pc!")
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_X( "blx    __dummy_thumb_subroutine_even",
+               ".thumb                         \n\t"
+               ".space 4                       \n\t"
+               ".type __dummy_thumb_subroutine_even, %%function \n\t"
+               "__dummy_thumb_subroutine_even: \n\t"
+               "mov    r0, pc                  \n\t"
+               "bx     lr                      \n\t"
+               ".arm                           \n\t"
+       )
+       TEST(   "blx    __dummy_thumb_subroutine_even")
+
+       TEST_X( "blx    __dummy_thumb_subroutine_odd",
+               ".thumb                         \n\t"
+               ".space 2                       \n\t"
+               ".type __dummy_thumb_subroutine_odd, %%function \n\t"
+               "__dummy_thumb_subroutine_odd:  \n\t"
+               "mov    r0, pc                  \n\t"
+               "bx     lr                      \n\t"
+               ".arm                           \n\t"
+       )
+       TEST(   "blx    __dummy_thumb_subroutine_odd")
+#endif /* __LINUX_ARM_ARCH__ >= 6 */
+
+       COPROCESSOR_INSTRUCTIONS_ST_LD("2","f")
+#if __LINUX_ARM_ARCH__ >= 6
+       COPROCESSOR_INSTRUCTIONS_MC_MR("2","f")
+#endif
+
+       TEST_GROUP("Miscellaneous instructions, memory hints, and Advanced SIMD instructions")
+
+#if __LINUX_ARM_ARCH__ >= 6
+       TEST_UNSUPPORTED("cps   0x13")
+       TEST_UNSUPPORTED("cpsie i")
+       TEST_UNSUPPORTED("cpsid i")
+       TEST_UNSUPPORTED("cpsie i,0x13")
+       TEST_UNSUPPORTED("cpsid i,0x13")
+       TEST_UNSUPPORTED("setend        le")
+       TEST_UNSUPPORTED("setend        be")
+#endif
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_P("pli     [r",0,0b,", #16]")
+       TEST(  "pli     [pc, #0]")
+       TEST_RR("pli    [r",12,0b,", r",0, 16,"]")
+       TEST_RR("pli    [r",0, 0b,", -r",12,16,", lsl #4]")
+#endif
+
+#if __LINUX_ARM_ARCH__ >= 5
+       TEST_P("pld     [r",0,32,", #-16]")
+       TEST(  "pld     [pc, #0]")
+       TEST_PR("pld    [r",7, 24, ", r",0, 16,"]")
+       TEST_PR("pld    [r",8, 24, ", -r",12,16,", lsl #4]")
+#endif
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_SUPPORTED(  ".word 0xf590f000      @ pldw [r0, #0]")
+       TEST_SUPPORTED(  ".word 0xf797f000      @ pldw  [r7, r0]")
+       TEST_SUPPORTED(  ".word 0xf798f18c      @ pldw  [r8, r12, lsl #3]");
+#endif
+
+#if __LINUX_ARM_ARCH__ >= 7
+       TEST_UNSUPPORTED("clrex")
+       TEST_UNSUPPORTED("dsb")
+       TEST_UNSUPPORTED("dmb")
+       TEST_UNSUPPORTED("isb")
+#endif
+
+       verbose("\n");
+}
+
diff --git a/arch/arm/kernel/kprobes-test-thumb.c b/arch/arm/kernel/kprobes-test-thumb.c
new file mode 100644 (file)
index 0000000..5e726c3
--- /dev/null
@@ -0,0 +1,1187 @@
+/*
+ * arch/arm/kernel/kprobes-test-thumb.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "kprobes-test.h"
+
+
+#define TEST_ISA "16"
+
+#define DONT_TEST_IN_ITBLOCK(tests)                    \
+       kprobe_test_flags |= TEST_FLAG_NO_ITBLOCK;      \
+       tests                                           \
+       kprobe_test_flags &= ~TEST_FLAG_NO_ITBLOCK;
+
+#define CONDITION_INSTRUCTIONS(cc_pos, tests)          \
+       kprobe_test_cc_position = cc_pos;               \
+       DONT_TEST_IN_ITBLOCK(tests)                     \
+       kprobe_test_cc_position = 0;
+
+#define TEST_ITBLOCK(code)                             \
+       kprobe_test_flags |= TEST_FLAG_FULL_ITBLOCK;    \
+       TESTCASE_START(code)                            \
+       TEST_ARG_END("")                                \
+       "50:    nop                     \n\t"           \
+       "1:     "code"                  \n\t"           \
+       "       mov r1, #0x11           \n\t"           \
+       "       mov r2, #0x22           \n\t"           \
+       "       mov r3, #0x33           \n\t"           \
+       "2:     nop                     \n\t"           \
+       TESTCASE_END                                    \
+       kprobe_test_flags &= ~TEST_FLAG_FULL_ITBLOCK;
+
+#define TEST_THUMB_TO_ARM_INTERWORK_P(code1, reg, val, code2)  \
+       TESTCASE_START(code1 #reg code2)                        \
+       TEST_ARG_PTR(reg, val)                                  \
+       TEST_ARG_REG(14, 99f+1)                                 \
+       TEST_ARG_MEM(15, 3f)                                    \
+       TEST_ARG_END("")                                        \
+       "       nop                     \n\t" /* To align 1f */ \
+       "50:    nop                     \n\t"                   \
+       "1:     "code1 #reg code2"      \n\t"                   \
+       "       bx      lr              \n\t"                   \
+       ".arm                           \n\t"                   \
+       "3:     adr     lr, 2f+1        \n\t"                   \
+       "       bx      lr              \n\t"                   \
+       ".thumb                         \n\t"                   \
+       "2:     nop                     \n\t"                   \
+       TESTCASE_END
+
+
+void kprobe_thumb16_test_cases(void)
+{
+       kprobe_test_flags = TEST_FLAG_NARROW_INSTR;
+
+       TEST_GROUP("Shift (immediate), add, subtract, move, and compare")
+
+       TEST_R(    "lsls        r7, r",0,VAL1,", #5")
+       TEST_R(    "lsls        r0, r",7,VAL2,", #11")
+       TEST_R(    "lsrs        r7, r",0,VAL1,", #5")
+       TEST_R(    "lsrs        r0, r",7,VAL2,", #11")
+       TEST_R(    "asrs        r7, r",0,VAL1,", #5")
+       TEST_R(    "asrs        r0, r",7,VAL2,", #11")
+       TEST_RR(   "adds        r2, r",0,VAL1,", r",7,VAL2,"")
+       TEST_RR(   "adds        r5, r",7,VAL2,", r",0,VAL2,"")
+       TEST_RR(   "subs        r2, r",0,VAL1,", r",7,VAL2,"")
+       TEST_RR(   "subs        r5, r",7,VAL2,", r",0,VAL2,"")
+       TEST_R(    "adds        r7, r",0,VAL1,", #5")
+       TEST_R(    "adds        r0, r",7,VAL2,", #2")
+       TEST_R(    "subs        r7, r",0,VAL1,", #5")
+       TEST_R(    "subs        r0, r",7,VAL2,", #2")
+       TEST(      "movs.n      r0, #0x5f")
+       TEST(      "movs.n      r7, #0xa0")
+       TEST_R(    "cmp.n       r",0,0x5e, ", #0x5f")
+       TEST_R(    "cmp.n       r",5,0x15f,", #0x5f")
+       TEST_R(    "cmp.n       r",7,0xa0, ", #0xa0")
+       TEST_R(    "adds.n      r",0,VAL1,", #0x5f")
+       TEST_R(    "adds.n      r",7,VAL2,", #0xa0")
+       TEST_R(    "subs.n      r",0,VAL1,", #0x5f")
+       TEST_R(    "subs.n      r",7,VAL2,", #0xa0")
+
+       TEST_GROUP("16-bit Thumb data-processing instructions")
+
+#define DATA_PROCESSING16(op,val)                      \
+       TEST_RR(   op"  r",0,VAL1,", r",7,val,"")       \
+       TEST_RR(   op"  r",7,VAL2,", r",0,val,"")
+
+       DATA_PROCESSING16("ands",0xf00f00ff)
+       DATA_PROCESSING16("eors",0xf00f00ff)
+       DATA_PROCESSING16("lsls",11)
+       DATA_PROCESSING16("lsrs",11)
+       DATA_PROCESSING16("asrs",11)
+       DATA_PROCESSING16("adcs",VAL2)
+       DATA_PROCESSING16("sbcs",VAL2)
+       DATA_PROCESSING16("rors",11)
+       DATA_PROCESSING16("tst",0xf00f00ff)
+       TEST_R("rsbs    r",0,VAL1,", #0")
+       TEST_R("rsbs    r",7,VAL2,", #0")
+       DATA_PROCESSING16("cmp",0xf00f00ff)
+       DATA_PROCESSING16("cmn",0xf00f00ff)
+       DATA_PROCESSING16("orrs",0xf00f00ff)
+       DATA_PROCESSING16("muls",VAL2)
+       DATA_PROCESSING16("bics",0xf00f00ff)
+       DATA_PROCESSING16("mvns",VAL2)
+
+       TEST_GROUP("Special data instructions and branch and exchange")
+
+       TEST_RR(  "add  r",0, VAL1,", r",7,VAL2,"")
+       TEST_RR(  "add  r",3, VAL2,", r",8,VAL3,"")
+       TEST_RR(  "add  r",8, VAL3,", r",0,VAL1,"")
+       TEST_R(   "add  sp"        ", r",8,-8,  "")
+       TEST_R(   "add  r",14,VAL1,", pc")
+       TEST_BF_R("add  pc"        ", r",0,2f-1f-8,"")
+       TEST_UNSUPPORTED(".short 0x44ff @ add pc, pc")
+
+       TEST_RR(  "cmp  r",3,VAL1,", r",8,VAL2,"")
+       TEST_RR(  "cmp  r",8,VAL2,", r",0,VAL1,"")
+       TEST_R(   "cmp  sp"       ", r",8,-8,  "")
+
+       TEST_R(   "mov  r0, r",7,VAL2,"")
+       TEST_R(   "mov  r3, r",8,VAL3,"")
+       TEST_R(   "mov  r8, r",0,VAL1,"")
+       TEST_P(   "mov  sp, r",8,-8,  "")
+       TEST(     "mov  lr, pc")
+       TEST_BF_R("mov  pc, r",0,2f,  "")
+
+       TEST_BF_R("bx   r",0, 2f+1,"")
+       TEST_BF_R("bx   r",14,2f+1,"")
+       TESTCASE_START("bx      pc")
+               TEST_ARG_REG(14, 99f+1)
+               TEST_ARG_END("")
+               "       nop                     \n\t" /* To align the bx pc*/
+               "50:    nop                     \n\t"
+               "1:     bx      pc              \n\t"
+               "       bx      lr              \n\t"
+               ".arm                           \n\t"
+               "       adr     lr, 2f+1        \n\t"
+               "       bx      lr              \n\t"
+               ".thumb                         \n\t"
+               "2:     nop                     \n\t"
+       TESTCASE_END
+
+       TEST_BF_R("blx  r",0, 2f+1,"")
+       TEST_BB_R("blx  r",14,2f+1,"")
+       TEST_UNSUPPORTED(".short 0x47f8 @ blx pc")
+
+       TEST_GROUP("Load from Literal Pool")
+
+       TEST_X( "ldr    r0, 3f",
+               ".align                                 \n\t"
+               "3:     .word   "__stringify(VAL1))
+       TEST_X( "ldr    r7, 3f",
+               ".space 128                             \n\t"
+               ".align                                 \n\t"
+               "3:     .word   "__stringify(VAL2))
+
+       TEST_GROUP("16-bit Thumb Load/store instructions")
+
+       TEST_RPR("str   r",0, VAL1,", [r",1, 24,", r",2,  48,"]")
+       TEST_RPR("str   r",7, VAL2,", [r",6, 24,", r",5,  48,"]")
+       TEST_RPR("strh  r",0, VAL1,", [r",1, 24,", r",2,  48,"]")
+       TEST_RPR("strh  r",7, VAL2,", [r",6, 24,", r",5,  48,"]")
+       TEST_RPR("strb  r",0, VAL1,", [r",1, 24,", r",2,  48,"]")
+       TEST_RPR("strb  r",7, VAL2,", [r",6, 24,", r",5,  48,"]")
+       TEST_PR( "ldrsb r0, [r",1, 24,", r",2,  48,"]")
+       TEST_PR( "ldrsb r7, [r",6, 24,", r",5,  50,"]")
+       TEST_PR( "ldr   r0, [r",1, 24,", r",2,  48,"]")
+       TEST_PR( "ldr   r7, [r",6, 24,", r",5,  48,"]")
+       TEST_PR( "ldrh  r0, [r",1, 24,", r",2,  48,"]")
+       TEST_PR( "ldrh  r7, [r",6, 24,", r",5,  50,"]")
+       TEST_PR( "ldrb  r0, [r",1, 24,", r",2,  48,"]")
+       TEST_PR( "ldrb  r7, [r",6, 24,", r",5,  50,"]")
+       TEST_PR( "ldrsh r0, [r",1, 24,", r",2,  48,"]")
+       TEST_PR( "ldrsh r7, [r",6, 24,", r",5,  50,"]")
+
+       TEST_RP("str    r",0, VAL1,", [r",1, 24,", #120]")
+       TEST_RP("str    r",7, VAL2,", [r",6, 24,", #120]")
+       TEST_P( "ldr    r0, [r",1, 24,", #120]")
+       TEST_P( "ldr    r7, [r",6, 24,", #120]")
+       TEST_RP("strb   r",0, VAL1,", [r",1, 24,", #30]")
+       TEST_RP("strb   r",7, VAL2,", [r",6, 24,", #30]")
+       TEST_P( "ldrb   r0, [r",1, 24,", #30]")
+       TEST_P( "ldrb   r7, [r",6, 24,", #30]")
+       TEST_RP("strh   r",0, VAL1,", [r",1, 24,", #60]")
+       TEST_RP("strh   r",7, VAL2,", [r",6, 24,", #60]")
+       TEST_P( "ldrh   r0, [r",1, 24,", #60]")
+       TEST_P( "ldrh   r7, [r",6, 24,", #60]")
+
+       TEST_R( "str    r",0, VAL1,", [sp, #0]")
+       TEST_R( "str    r",7, VAL2,", [sp, #160]")
+       TEST(   "ldr    r0, [sp, #0]")
+       TEST(   "ldr    r7, [sp, #160]")
+
+       TEST_RP("str    r",0, VAL1,", [r",0, 24,"]")
+       TEST_P( "ldr    r0, [r",0, 24,"]")
+
+       TEST_GROUP("Generate PC-/SP-relative address")
+
+       TEST("add       r0, pc, #4")
+       TEST("add       r7, pc, #1020")
+       TEST("add       r0, sp, #4")
+       TEST("add       r7, sp, #1020")
+
+       TEST_GROUP("Miscellaneous 16-bit instructions")
+
+       TEST_UNSUPPORTED( "cpsie        i")
+       TEST_UNSUPPORTED( "cpsid        i")
+       TEST_UNSUPPORTED( "setend       le")
+       TEST_UNSUPPORTED( "setend       be")
+
+       TEST("add       sp, #"__stringify(TEST_MEMORY_SIZE)) /* Assumes TEST_MEMORY_SIZE < 0x400 */
+       TEST("sub       sp, #0x7f*4")
+
+DONT_TEST_IN_ITBLOCK(
+       TEST_BF_R(  "cbnz       r",0,0, ", 2f")
+       TEST_BF_R(  "cbz        r",2,-1,", 2f")
+       TEST_BF_RX( "cbnz       r",4,1, ", 2f",0x20)
+       TEST_BF_RX( "cbz        r",7,0, ", 2f",0x40)
+)
+       TEST_R("sxth    r0, r",7, HH1,"")
+       TEST_R("sxth    r7, r",0, HH2,"")
+       TEST_R("sxtb    r0, r",7, HH1,"")
+       TEST_R("sxtb    r7, r",0, HH2,"")
+       TEST_R("uxth    r0, r",7, HH1,"")
+       TEST_R("uxth    r7, r",0, HH2,"")
+       TEST_R("uxtb    r0, r",7, HH1,"")
+       TEST_R("uxtb    r7, r",0, HH2,"")
+       TEST_R("rev     r0, r",7, VAL1,"")
+       TEST_R("rev     r7, r",0, VAL2,"")
+       TEST_R("rev16   r0, r",7, VAL1,"")
+       TEST_R("rev16   r7, r",0, VAL2,"")
+       TEST_UNSUPPORTED(".short 0xba80")
+       TEST_UNSUPPORTED(".short 0xbabf")
+       TEST_R("revsh   r0, r",7, VAL1,"")
+       TEST_R("revsh   r7, r",0, VAL2,"")
+
+#define TEST_POPPC(code, offset)       \
+       TESTCASE_START(code)            \
+       TEST_ARG_PTR(13, offset)        \
+       TEST_ARG_END("")                \
+       TEST_BRANCH_F(code,0)           \
+       TESTCASE_END
+
+       TEST("push      {r0}")
+       TEST("push      {r7}")
+       TEST("push      {r14}")
+       TEST("push      {r0-r7,r14}")
+       TEST("push      {r0,r2,r4,r6,r14}")
+       TEST("push      {r1,r3,r5,r7}")
+       TEST("pop       {r0}")
+       TEST("pop       {r7}")
+       TEST("pop       {r0,r2,r4,r6}")
+       TEST_POPPC("pop {pc}",15*4)
+       TEST_POPPC("pop {r0-r7,pc}",7*4)
+       TEST_POPPC("pop {r1,r3,r5,r7,pc}",11*4)
+       TEST_THUMB_TO_ARM_INTERWORK_P("pop      {pc}    @ ",13,15*4,"")
+       TEST_THUMB_TO_ARM_INTERWORK_P("pop      {r0-r7,pc}      @ ",13,7*4,"")
+
+       TEST_UNSUPPORTED("bkpt.n        0")
+       TEST_UNSUPPORTED("bkpt.n        255")
+
+       TEST_SUPPORTED("yield")
+       TEST("sev")
+       TEST("nop")
+       TEST("wfi")
+       TEST_SUPPORTED("wfe")
+       TEST_UNSUPPORTED(".short 0xbf50") /* Unassigned hints */
+       TEST_UNSUPPORTED(".short 0xbff0") /* Unassigned hints */
+
+#define TEST_IT(code, code2)                   \
+       TESTCASE_START(code)                    \
+       TEST_ARG_END("")                        \
+       "50:    nop                     \n\t"   \
+       "1:     "code"                  \n\t"   \
+       "       "code2"                 \n\t"   \
+       "2:     nop                     \n\t"   \
+       TESTCASE_END
+
+DONT_TEST_IN_ITBLOCK(
+       TEST_IT("it     eq","moveq r0,#0")
+       TEST_IT("it     vc","movvc r0,#0")
+       TEST_IT("it     le","movle r0,#0")
+       TEST_IT("ite    eq","moveq r0,#0\n\t  movne r1,#1")
+       TEST_IT("itet   vc","movvc r0,#0\n\t  movvs r1,#1\n\t  movvc r2,#2")
+       TEST_IT("itete  le","movle r0,#0\n\t  movgt r1,#1\n\t  movle r2,#2\n\t  movgt r3,#3")
+       TEST_IT("itttt  le","movle r0,#0\n\t  movle r1,#1\n\t  movle r2,#2\n\t  movle r3,#3")
+       TEST_IT("iteee  le","movle r0,#0\n\t  movgt r1,#1\n\t  movgt r2,#2\n\t  movgt r3,#3")
+)
+
+       TEST_GROUP("Load and store multiple")
+
+       TEST_P("ldmia   r",4, 16*4,"!, {r0,r7}")
+       TEST_P("ldmia   r",7, 16*4,"!, {r0-r6}")
+       TEST_P("stmia   r",4, 16*4,"!, {r0,r7}")
+       TEST_P("stmia   r",0, 16*4,"!, {r0-r7}")
+
+       TEST_GROUP("Conditional branch and Supervisor Call instructions")
+
+CONDITION_INSTRUCTIONS(8,
+       TEST_BF("beq    2f")
+       TEST_BB("bne    2b")
+       TEST_BF("bgt    2f")
+       TEST_BB("blt    2b")
+)
+       TEST_UNSUPPORTED(".short 0xde00")
+       TEST_UNSUPPORTED(".short 0xdeff")
+       TEST_UNSUPPORTED("svc   #0x00")
+       TEST_UNSUPPORTED("svc   #0xff")
+
+       TEST_GROUP("Unconditional branch")
+
+       TEST_BF(  "b    2f")
+       TEST_BB(  "b    2b")
+       TEST_BF_X("b    2f", 0x400)
+       TEST_BB_X("b    2b", 0x400)
+
+       TEST_GROUP("Testing instructions in IT blocks")
+
+       TEST_ITBLOCK("subs.n r0, r0")
+
+       verbose("\n");
+}
+
+
+void kprobe_thumb32_test_cases(void)
+{
+       kprobe_test_flags = 0;
+
+       TEST_GROUP("Load/store multiple")
+
+       TEST_UNSUPPORTED("rfedb sp")
+       TEST_UNSUPPORTED("rfeia sp")
+       TEST_UNSUPPORTED("rfedb sp!")
+       TEST_UNSUPPORTED("rfeia sp!")
+
+       TEST_P(   "stmia        r",0, 16*4,", {r0,r8}")
+       TEST_P(   "stmia        r",4, 16*4,", {r0-r12,r14}")
+       TEST_P(   "stmia        r",7, 16*4,"!, {r8-r12,r14}")
+       TEST_P(   "stmia        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+
+       TEST_P(   "ldmia        r",0, 16*4,", {r0,r8}")
+       TEST_P(   "ldmia        r",4, 0,   ", {r0-r12,r14}")
+       TEST_BF_P("ldmia        r",5, 8*4, "!, {r6-r12,r15}")
+       TEST_P(   "ldmia        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_BF_P("ldmia        r",14,14*4,"!, {r4,pc}")
+
+       TEST_P(   "stmdb        r",0, 16*4,", {r0,r8}")
+       TEST_P(   "stmdb        r",4, 16*4,", {r0-r12,r14}")
+       TEST_P(   "stmdb        r",5, 16*4,"!, {r8-r12,r14}")
+       TEST_P(   "stmdb        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+
+       TEST_P(   "ldmdb        r",0, 16*4,", {r0,r8}")
+       TEST_P(   "ldmdb        r",4, 16*4,", {r0-r12,r14}")
+       TEST_BF_P("ldmdb        r",5, 16*4,"!, {r6-r12,r15}")
+       TEST_P(   "ldmdb        r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}")
+       TEST_BF_P("ldmdb        r",14,16*4,"!, {r4,pc}")
+
+       TEST_P(   "stmdb        r",13,16*4,"!, {r3-r12,lr}")
+       TEST_P(   "stmdb        r",13,16*4,"!, {r3-r12}")
+       TEST_P(   "stmdb        r",2, 16*4,", {r3-r12,lr}")
+       TEST_P(   "stmdb        r",13,16*4,"!, {r2-r12,lr}")
+       TEST_P(   "stmdb        r",0, 16*4,", {r0-r12}")
+       TEST_P(   "stmdb        r",0, 16*4,", {r0-r12,lr}")
+
+       TEST_BF_P("ldmia        r",13,5*4, "!, {r3-r12,pc}")
+       TEST_P(   "ldmia        r",13,5*4, "!, {r3-r12}")
+       TEST_BF_P("ldmia        r",2, 5*4, "!, {r3-r12,pc}")
+       TEST_BF_P("ldmia        r",13,4*4, "!, {r2-r12,pc}")
+       TEST_P(   "ldmia        r",0, 16*4,", {r0-r12}")
+       TEST_P(   "ldmia        r",0, 16*4,", {r0-r12,lr}")
+
+       TEST_THUMB_TO_ARM_INTERWORK_P("ldmia    r",0,14*4,", {r12,pc}")
+       TEST_THUMB_TO_ARM_INTERWORK_P("ldmia    r",13,2*4,", {r0-r12,pc}")
+
+       TEST_UNSUPPORTED(".short 0xe88f,0x0101  @ stmia pc, {r0,r8}")
+       TEST_UNSUPPORTED(".short 0xe92f,0x5f00  @ stmdb pc!, {r8-r12,r14}")
+       TEST_UNSUPPORTED(".short 0xe8bd,0xc000  @ ldmia r13!, {r14,pc}")
+       TEST_UNSUPPORTED(".short 0xe93e,0xc000  @ ldmdb r14!, {r14,pc}")
+       TEST_UNSUPPORTED(".short 0xe8a7,0x3f00  @ stmia r7!, {r8-r12,sp}")
+       TEST_UNSUPPORTED(".short 0xe8a7,0x9f00  @ stmia r7!, {r8-r12,pc}")
+       TEST_UNSUPPORTED(".short 0xe93e,0x2010  @ ldmdb r14!, {r4,sp}")
+
+       TEST_GROUP("Load/store double or exclusive, table branch")
+
+       TEST_P(  "ldrd  r0, r1, [r",1, 24,", #-16]")
+       TEST(    "ldrd  r12, r14, [sp, #16]")
+       TEST_P(  "ldrd  r1, r0, [r",7, 24,", #-16]!")
+       TEST(    "ldrd  r14, r12, [sp, #16]!")
+       TEST_P(  "ldrd  r1, r0, [r",7, 24,"], #16")
+       TEST(    "ldrd  r7, r8, [sp], #-16")
+
+       TEST_X( "ldrd   r12, r14, 3f",
+               ".align 3                               \n\t"
+               "3:     .word   "__stringify(VAL1)"     \n\t"
+               "       .word   "__stringify(VAL2))
+
+       TEST_UNSUPPORTED(".short 0xe9ff,0xec04  @ ldrd  r14, r12, [pc, #16]!")
+       TEST_UNSUPPORTED(".short 0xe8ff,0xec04  @ ldrd  r14, r12, [pc], #16")
+       TEST_UNSUPPORTED(".short 0xe9d4,0xd800  @ ldrd  sp, r8, [r4]")
+       TEST_UNSUPPORTED(".short 0xe9d4,0xf800  @ ldrd  pc, r8, [r4]")
+       TEST_UNSUPPORTED(".short 0xe9d4,0x7d00  @ ldrd  r7, sp, [r4]")
+       TEST_UNSUPPORTED(".short 0xe9d4,0x7f00  @ ldrd  r7, pc, [r4]")
+
+       TEST_RRP("strd  r",0, VAL1,", r",1, VAL2,", [r",1, 24,", #-16]")
+       TEST_RR( "strd  r",12,VAL2,", r",14,VAL1,", [sp, #16]")
+       TEST_RRP("strd  r",1, VAL1,", r",0, VAL2,", [r",7, 24,", #-16]!")
+       TEST_RR( "strd  r",14,VAL2,", r",12,VAL1,", [sp, #16]!")
+       TEST_RRP("strd  r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16")
+       TEST_RR( "strd  r",7, VAL2,", r",8, VAL1,", [sp], #-16")
+       TEST_UNSUPPORTED(".short 0xe9ef,0xec04  @ strd  r14, r12, [pc, #16]!")
+       TEST_UNSUPPORTED(".short 0xe8ef,0xec04  @ strd  r14, r12, [pc], #16")
+
+       TEST_RX("tbb    [pc, r",0, (9f-(1f+4)),"]",
+               "9:                     \n\t"
+               ".byte  (2f-1b-4)>>1    \n\t"
+               ".byte  (3f-1b-4)>>1    \n\t"
+               "3:     mvn     r0, r0  \n\t"
+               "2:     nop             \n\t")
+
+       TEST_RX("tbb    [pc, r",4, (9f-(1f+4)+1),"]",
+               "9:                     \n\t"
+               ".byte  (2f-1b-4)>>1    \n\t"
+               ".byte  (3f-1b-4)>>1    \n\t"
+               "3:     mvn     r0, r0  \n\t"
+               "2:     nop             \n\t")
+
+       TEST_RRX("tbb   [r",1,9f,", r",2,0,"]",
+               "9:                     \n\t"
+               ".byte  (2f-1b-4)>>1    \n\t"
+               ".byte  (3f-1b-4)>>1    \n\t"
+               "3:     mvn     r0, r0  \n\t"
+               "2:     nop             \n\t")
+
+       TEST_RX("tbh    [pc, r",7, (9f-(1f+4))>>1,"]",
+               "9:                     \n\t"
+               ".short (2f-1b-4)>>1    \n\t"
+               ".short (3f-1b-4)>>1    \n\t"
+               "3:     mvn     r0, r0  \n\t"
+               "2:     nop             \n\t")
+
+       TEST_RX("tbh    [pc, r",12, ((9f-(1f+4))>>1)+1,"]",
+               "9:                     \n\t"
+               ".short (2f-1b-4)>>1    \n\t"
+               ".short (3f-1b-4)>>1    \n\t"
+               "3:     mvn     r0, r0  \n\t"
+               "2:     nop             \n\t")
+
+       TEST_RRX("tbh   [r",1,9f, ", r",14,1,"]",
+               "9:                     \n\t"
+               ".short (2f-1b-4)>>1    \n\t"
+               ".short (3f-1b-4)>>1    \n\t"
+               "3:     mvn     r0, r0  \n\t"
+               "2:     nop             \n\t")
+
+       TEST_UNSUPPORTED(".short 0xe8d1,0xf01f  @ tbh [r1, pc]")
+       TEST_UNSUPPORTED(".short 0xe8d1,0xf01d  @ tbh [r1, sp]")
+       TEST_UNSUPPORTED(".short 0xe8dd,0xf012  @ tbh [sp, r2]")
+
+       TEST_UNSUPPORTED("strexb        r0, r1, [r2]")
+       TEST_UNSUPPORTED("strexh        r0, r1, [r2]")
+       TEST_UNSUPPORTED("strexd        r0, r1, [r2]")
+       TEST_UNSUPPORTED("ldrexb        r0, [r1]")
+       TEST_UNSUPPORTED("ldrexh        r0, [r1]")
+       TEST_UNSUPPORTED("ldrexd        r0, [r1]")
+
+       TEST_GROUP("Data-processing (shifted register) and (modified immediate)")
+
+#define _DATA_PROCESSING32_DNM(op,s,val)                                       \
+       TEST_RR(op s".w r0,  r",1, VAL1,", r",2, val, "")                       \
+       TEST_RR(op s"   r1,  r",1, VAL1,", r",2, val, ", lsl #3")               \
+       TEST_RR(op s"   r2,  r",3, VAL1,", r",2, val, ", lsr #4")               \
+       TEST_RR(op s"   r3,  r",3, VAL1,", r",2, val, ", asr #5")               \
+       TEST_RR(op s"   r4,  r",5, VAL1,", r",2, N(val),", asr #6")             \
+       TEST_RR(op s"   r5,  r",5, VAL1,", r",2, val, ", ror #7")               \
+       TEST_RR(op s"   r8,  r",9, VAL1,", r",10,val, ", rrx")                  \
+       TEST_R( op s"   r0,  r",11,VAL1,", #0x00010001")                        \
+       TEST_R( op s"   r11, r",0, VAL1,", #0xf5000000")                        \
+       TEST_R( op s"   r7,  r",8, VAL2,", #0x000af000")
+
+#define DATA_PROCESSING32_DNM(op,val)          \
+       _DATA_PROCESSING32_DNM(op,"",val)       \
+       _DATA_PROCESSING32_DNM(op,"s",val)
+
+#define DATA_PROCESSING32_NM(op,val)                                   \
+       TEST_RR(op".w   r",1, VAL1,", r",2, val, "")                    \
+       TEST_RR(op"     r",1, VAL1,", r",2, val, ", lsl #3")            \
+       TEST_RR(op"     r",3, VAL1,", r",2, val, ", lsr #4")            \
+       TEST_RR(op"     r",3, VAL1,", r",2, val, ", asr #5")            \
+       TEST_RR(op"     r",5, VAL1,", r",2, N(val),", asr #6")          \
+       TEST_RR(op"     r",5, VAL1,", r",2, val, ", ror #7")            \
+       TEST_RR(op"     r",9, VAL1,", r",10,val, ", rrx")               \
+       TEST_R( op"     r",11,VAL1,", #0x00010001")                     \
+       TEST_R( op"     r",0, VAL1,", #0xf5000000")                     \
+       TEST_R( op"     r",8, VAL2,", #0x000af000")
+
+#define _DATA_PROCESSING32_DM(op,s,val)                                \
+       TEST_R( op s".w r0,  r",14, val, "")                    \
+       TEST_R( op s"   r1,  r",12, val, ", lsl #3")            \
+       TEST_R( op s"   r2,  r",11, val, ", lsr #4")            \
+       TEST_R( op s"   r3,  r",10, val, ", asr #5")            \
+       TEST_R( op s"   r4,  r",9, N(val),", asr #6")           \
+       TEST_R( op s"   r5,  r",8, val, ", ror #7")             \
+       TEST_R( op s"   r8,  r",7,val, ", rrx")                 \
+       TEST(   op s"   r0,  #0x00010001")                      \
+       TEST(   op s"   r11, #0xf5000000")                      \
+       TEST(   op s"   r7,  #0x000af000")                      \
+       TEST(   op s"   r4,  #0x00005a00")
+
+#define DATA_PROCESSING32_DM(op,val)           \
+       _DATA_PROCESSING32_DM(op,"",val)        \
+       _DATA_PROCESSING32_DM(op,"s",val)
+
+       DATA_PROCESSING32_DNM("and",0xf00f00ff)
+       DATA_PROCESSING32_NM("tst",0xf00f00ff)
+       DATA_PROCESSING32_DNM("bic",0xf00f00ff)
+       DATA_PROCESSING32_DNM("orr",0xf00f00ff)
+       DATA_PROCESSING32_DM("mov",VAL2)
+       DATA_PROCESSING32_DNM("orn",0xf00f00ff)
+       DATA_PROCESSING32_DM("mvn",VAL2)
+       DATA_PROCESSING32_DNM("eor",0xf00f00ff)
+       DATA_PROCESSING32_NM("teq",0xf00f00ff)
+       DATA_PROCESSING32_DNM("add",VAL2)
+       DATA_PROCESSING32_NM("cmn",VAL2)
+       DATA_PROCESSING32_DNM("adc",VAL2)
+       DATA_PROCESSING32_DNM("sbc",VAL2)
+       DATA_PROCESSING32_DNM("sub",VAL2)
+       DATA_PROCESSING32_NM("cmp",VAL2)
+       DATA_PROCESSING32_DNM("rsb",VAL2)
+
+       TEST_RR("pkhbt  r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR("pkhbt  r14,r",12, HH1,", r",10,HH2,", lsl #2")
+       TEST_RR("pkhtb  r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR("pkhtb  r14,r",12, HH1,", r",10,HH2,", asr #2")
+
+       TEST_UNSUPPORTED(".short 0xea17,0x0f0d  @ tst.w r7, sp")
+       TEST_UNSUPPORTED(".short 0xea17,0x0f0f  @ tst.w r7, pc")
+       TEST_UNSUPPORTED(".short 0xea1d,0x0f07  @ tst.w sp, r7")
+       TEST_UNSUPPORTED(".short 0xea1f,0x0f07  @ tst.w pc, r7")
+       TEST_UNSUPPORTED(".short 0xf01d,0x1f08  @ tst sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf01f,0x1f08  @ tst pc, #0x00080008")
+
+       TEST_UNSUPPORTED(".short 0xea97,0x0f0d  @ teq.w r7, sp")
+       TEST_UNSUPPORTED(".short 0xea97,0x0f0f  @ teq.w r7, pc")
+       TEST_UNSUPPORTED(".short 0xea9d,0x0f07  @ teq.w sp, r7")
+       TEST_UNSUPPORTED(".short 0xea9f,0x0f07  @ teq.w pc, r7")
+       TEST_UNSUPPORTED(".short 0xf09d,0x1f08  @ tst sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf09f,0x1f08  @ tst pc, #0x00080008")
+
+       TEST_UNSUPPORTED(".short 0xeb17,0x0f0d  @ cmn.w r7, sp")
+       TEST_UNSUPPORTED(".short 0xeb17,0x0f0f  @ cmn.w r7, pc")
+       TEST_P("cmn.w   sp, r",7,0,"")
+       TEST_UNSUPPORTED(".short 0xeb1f,0x0f07  @ cmn.w pc, r7")
+       TEST(  "cmn     sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf11f,0x1f08  @ cmn pc, #0x00080008")
+
+       TEST_UNSUPPORTED(".short 0xebb7,0x0f0d  @ cmp.w r7, sp")
+       TEST_UNSUPPORTED(".short 0xebb7,0x0f0f  @ cmp.w r7, pc")
+       TEST_P("cmp.w   sp, r",7,0,"")
+       TEST_UNSUPPORTED(".short 0xebbf,0x0f07  @ cmp.w pc, r7")
+       TEST(  "cmp     sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf1bf,0x1f08  @ cmp pc, #0x00080008")
+
+       TEST_UNSUPPORTED(".short 0xea5f,0x070d  @ movs.w r7, sp")
+       TEST_UNSUPPORTED(".short 0xea5f,0x070f  @ movs.w r7, pc")
+       TEST_UNSUPPORTED(".short 0xea5f,0x0d07  @ movs.w sp, r7")
+       TEST_UNSUPPORTED(".short 0xea4f,0x0f07  @ mov.w  pc, r7")
+       TEST_UNSUPPORTED(".short 0xf04f,0x1d08  @ mov sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf04f,0x1f08  @ mov pc, #0x00080008")
+
+       TEST_R("add.w   r0, sp, r",1, 4,"")
+       TEST_R("adds    r0, sp, r",1, 4,", asl #3")
+       TEST_R("add     r0, sp, r",1, 4,", asl #4")
+       TEST_R("add     r0, sp, r",1, 16,", ror #1")
+       TEST_R("add.w   sp, sp, r",1, 4,"")
+       TEST_R("add     sp, sp, r",1, 4,", asl #3")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x1d01  @ add sp, sp, r1, asl #4")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x0d71  @ add sp, sp, r1, ror #1")
+       TEST(  "add.w   r0, sp, #24")
+       TEST(  "add.w   sp, sp, #24")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x0f01  @ add pc, sp, r1")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x000f  @ add r0, sp, pc")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x000d  @ add r0, sp, sp")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x0d0f  @ add sp, sp, pc")
+       TEST_UNSUPPORTED(".short 0xeb0d,0x0d0d  @ add sp, sp, sp")
+
+       TEST_R("sub.w   r0, sp, r",1, 4,"")
+       TEST_R("subs    r0, sp, r",1, 4,", asl #3")
+       TEST_R("sub     r0, sp, r",1, 4,", asl #4")
+       TEST_R("sub     r0, sp, r",1, 16,", ror #1")
+       TEST_R("sub.w   sp, sp, r",1, 4,"")
+       TEST_R("sub     sp, sp, r",1, 4,", asl #3")
+       TEST_UNSUPPORTED(".short 0xebad,0x1d01  @ sub sp, sp, r1, asl #4")
+       TEST_UNSUPPORTED(".short 0xebad,0x0d71  @ sub sp, sp, r1, ror #1")
+       TEST_UNSUPPORTED(".short 0xebad,0x0f01  @ sub pc, sp, r1")
+       TEST(  "sub.w   r0, sp, #24")
+       TEST(  "sub.w   sp, sp, #24")
+
+       TEST_UNSUPPORTED(".short 0xea02,0x010f  @ and r1, r2, pc")
+       TEST_UNSUPPORTED(".short 0xea0f,0x0103  @ and r1, pc, r3")
+       TEST_UNSUPPORTED(".short 0xea02,0x0f03  @ and pc, r2, r3")
+       TEST_UNSUPPORTED(".short 0xea02,0x010d  @ and r1, r2, sp")
+       TEST_UNSUPPORTED(".short 0xea0d,0x0103  @ and r1, sp, r3")
+       TEST_UNSUPPORTED(".short 0xea02,0x0d03  @ and sp, r2, r3")
+       TEST_UNSUPPORTED(".short 0xf00d,0x1108  @ and r1, sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf00f,0x1108  @ and r1, pc, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf002,0x1d08  @ and sp, r8, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf002,0x1f08  @ and pc, r8, #0x00080008")
+
+       TEST_UNSUPPORTED(".short 0xeb02,0x010f  @ add r1, r2, pc")
+       TEST_UNSUPPORTED(".short 0xeb0f,0x0103  @ add r1, pc, r3")
+       TEST_UNSUPPORTED(".short 0xeb02,0x0f03  @ add pc, r2, r3")
+       TEST_UNSUPPORTED(".short 0xeb02,0x010d  @ add r1, r2, sp")
+       TEST_SUPPORTED(  ".short 0xeb0d,0x0103  @ add r1, sp, r3")
+       TEST_UNSUPPORTED(".short 0xeb02,0x0d03  @ add sp, r2, r3")
+       TEST_SUPPORTED(  ".short 0xf10d,0x1108  @ add r1, sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf10d,0x1f08  @ add pc, sp, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf10f,0x1108  @ add r1, pc, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf102,0x1d08  @ add sp, r8, #0x00080008")
+       TEST_UNSUPPORTED(".short 0xf102,0x1f08  @ add pc, r8, #0x00080008")
+
+       TEST_UNSUPPORTED(".short 0xeaa0,0x0000")
+       TEST_UNSUPPORTED(".short 0xeaf0,0x0000")
+       TEST_UNSUPPORTED(".short 0xeb20,0x0000")
+       TEST_UNSUPPORTED(".short 0xeb80,0x0000")
+       TEST_UNSUPPORTED(".short 0xebe0,0x0000")
+
+       TEST_UNSUPPORTED(".short 0xf0a0,0x0000")
+       TEST_UNSUPPORTED(".short 0xf0c0,0x0000")
+       TEST_UNSUPPORTED(".short 0xf0f0,0x0000")
+       TEST_UNSUPPORTED(".short 0xf120,0x0000")
+       TEST_UNSUPPORTED(".short 0xf180,0x0000")
+       TEST_UNSUPPORTED(".short 0xf1e0,0x0000")
+
+       TEST_GROUP("Coprocessor instructions")
+
+       TEST_UNSUPPORTED(".short 0xec00,0x0000")
+       TEST_UNSUPPORTED(".short 0xeff0,0x0000")
+       TEST_UNSUPPORTED(".short 0xfc00,0x0000")
+       TEST_UNSUPPORTED(".short 0xfff0,0x0000")
+
+       TEST_GROUP("Data-processing (plain binary immediate)")
+
+       TEST_R("addw    r0,  r",1, VAL1,", #0x123")
+       TEST(  "addw    r14, sp, #0xf5a")
+       TEST(  "addw    sp, sp, #0x20")
+       TEST(  "addw    r7,  pc, #0x888")
+       TEST_UNSUPPORTED(".short 0xf20f,0x1f20  @ addw pc, pc, #0x120")
+       TEST_UNSUPPORTED(".short 0xf20d,0x1f20  @ addw pc, sp, #0x120")
+       TEST_UNSUPPORTED(".short 0xf20f,0x1d20  @ addw sp, pc, #0x120")
+       TEST_UNSUPPORTED(".short 0xf200,0x1d20  @ addw sp, r0, #0x120")
+
+       TEST_R("subw    r0,  r",1, VAL1,", #0x123")
+       TEST(  "subw    r14, sp, #0xf5a")
+       TEST(  "subw    sp, sp, #0x20")
+       TEST(  "subw    r7,  pc, #0x888")
+       TEST_UNSUPPORTED(".short 0xf2af,0x1f20  @ subw pc, pc, #0x120")
+       TEST_UNSUPPORTED(".short 0xf2ad,0x1f20  @ subw pc, sp, #0x120")
+       TEST_UNSUPPORTED(".short 0xf2af,0x1d20  @ subw sp, pc, #0x120")
+       TEST_UNSUPPORTED(".short 0xf2a0,0x1d20  @ subw sp, r0, #0x120")
+
+       TEST("movw      r0, #0")
+       TEST("movw      r0, #0xffff")
+       TEST("movw      lr, #0xffff")
+       TEST_UNSUPPORTED(".short 0xf240,0x0d00  @ movw sp, #0")
+       TEST_UNSUPPORTED(".short 0xf240,0x0f00  @ movw pc, #0")
+
+       TEST_R("movt    r",0, VAL1,", #0")
+       TEST_R("movt    r",0, VAL2,", #0xffff")
+       TEST_R("movt    r",14,VAL1,", #0xffff")
+       TEST_UNSUPPORTED(".short 0xf2c0,0x0d00  @ movt sp, #0")
+       TEST_UNSUPPORTED(".short 0xf2c0,0x0f00  @ movt pc, #0")
+
+       TEST_R(     "ssat       r0, #24, r",0,   VAL1,"")
+       TEST_R(     "ssat       r14, #24, r",12, VAL2,"")
+       TEST_R(     "ssat       r0, #24, r",0,   VAL1,", lsl #8")
+       TEST_R(     "ssat       r14, #24, r",12, VAL2,", asr #8")
+       TEST_UNSUPPORTED(".short 0xf30c,0x0d17  @ ssat  sp, #24, r12")
+       TEST_UNSUPPORTED(".short 0xf30c,0x0f17  @ ssat  pc, #24, r12")
+       TEST_UNSUPPORTED(".short 0xf30d,0x0c17  @ ssat  r12, #24, sp")
+       TEST_UNSUPPORTED(".short 0xf30f,0x0c17  @ ssat  r12, #24, pc")
+
+       TEST_R(     "usat       r0, #24, r",0,   VAL1,"")
+       TEST_R(     "usat       r14, #24, r",12, VAL2,"")
+       TEST_R(     "usat       r0, #24, r",0,   VAL1,", lsl #8")
+       TEST_R(     "usat       r14, #24, r",12, VAL2,", asr #8")
+       TEST_UNSUPPORTED(".short 0xf38c,0x0d17  @ usat  sp, #24, r12")
+       TEST_UNSUPPORTED(".short 0xf38c,0x0f17  @ usat  pc, #24, r12")
+       TEST_UNSUPPORTED(".short 0xf38d,0x0c17  @ usat  r12, #24, sp")
+       TEST_UNSUPPORTED(".short 0xf38f,0x0c17  @ usat  r12, #24, pc")
+
+       TEST_R(     "ssat16     r0, #12, r",0,   HH1,"")
+       TEST_R(     "ssat16     r14, #12, r",12, HH2,"")
+       TEST_UNSUPPORTED(".short 0xf32c,0x0d0b  @ ssat16        sp, #12, r12")
+       TEST_UNSUPPORTED(".short 0xf32c,0x0f0b  @ ssat16        pc, #12, r12")
+       TEST_UNSUPPORTED(".short 0xf32d,0x0c0b  @ ssat16        r12, #12, sp")
+       TEST_UNSUPPORTED(".short 0xf32f,0x0c0b  @ ssat16        r12, #12, pc")
+
+       TEST_R(     "usat16     r0, #12, r",0,   HH1,"")
+       TEST_R(     "usat16     r14, #12, r",12, HH2,"")
+       TEST_UNSUPPORTED(".short 0xf3ac,0x0d0b  @ usat16        sp, #12, r12")
+       TEST_UNSUPPORTED(".short 0xf3ac,0x0f0b  @ usat16        pc, #12, r12")
+       TEST_UNSUPPORTED(".short 0xf3ad,0x0c0b  @ usat16        r12, #12, sp")
+       TEST_UNSUPPORTED(".short 0xf3af,0x0c0b  @ usat16        r12, #12, pc")
+
+       TEST_R(     "sbfx       r0, r",0  , VAL1,", #0, #31")
+       TEST_R(     "sbfx       r14, r",12, VAL2,", #8, #16")
+       TEST_R(     "sbfx       r4, r",10,  VAL1,", #16, #15")
+       TEST_UNSUPPORTED(".short 0xf34c,0x2d0f  @ sbfx  sp, r12, #8, #16")
+       TEST_UNSUPPORTED(".short 0xf34c,0x2f0f  @ sbfx  pc, r12, #8, #16")
+       TEST_UNSUPPORTED(".short 0xf34d,0x2c0f  @ sbfx  r12, sp, #8, #16")
+       TEST_UNSUPPORTED(".short 0xf34f,0x2c0f  @ sbfx  r12, pc, #8, #16")
+
+       TEST_R(     "ubfx       r0, r",0  , VAL1,", #0, #31")
+       TEST_R(     "ubfx       r14, r",12, VAL2,", #8, #16")
+       TEST_R(     "ubfx       r4, r",10,  VAL1,", #16, #15")
+       TEST_UNSUPPORTED(".short 0xf3cc,0x2d0f  @ ubfx  sp, r12, #8, #16")
+       TEST_UNSUPPORTED(".short 0xf3cc,0x2f0f  @ ubfx  pc, r12, #8, #16")
+       TEST_UNSUPPORTED(".short 0xf3cd,0x2c0f  @ ubfx  r12, sp, #8, #16")
+       TEST_UNSUPPORTED(".short 0xf3cf,0x2c0f  @ ubfx  r12, pc, #8, #16")
+
+       TEST_R(     "bfc        r",0, VAL1,", #4, #20")
+       TEST_R(     "bfc        r",14,VAL2,", #4, #20")
+       TEST_R(     "bfc        r",7, VAL1,", #0, #31")
+       TEST_R(     "bfc        r",8, VAL2,", #0, #31")
+       TEST_UNSUPPORTED(".short 0xf36f,0x0d1e  @ bfc   sp, #0, #31")
+       TEST_UNSUPPORTED(".short 0xf36f,0x0f1e  @ bfc   pc, #0, #31")
+
+       TEST_RR(    "bfi        r",0, VAL1,", r",0  , VAL2,", #0, #31")
+       TEST_RR(    "bfi        r",12,VAL1,", r",14 , VAL2,", #4, #20")
+       TEST_UNSUPPORTED(".short 0xf36e,0x1d17  @ bfi   sp, r14, #4, #20")
+       TEST_UNSUPPORTED(".short 0xf36e,0x1f17  @ bfi   pc, r14, #4, #20")
+       TEST_UNSUPPORTED(".short 0xf36d,0x1e17  @ bfi   r14, sp, #4, #20")
+
+       TEST_GROUP("Branches and miscellaneous control")
+
+CONDITION_INSTRUCTIONS(22,
+       TEST_BF("beq.w  2f")
+       TEST_BB("bne.w  2b")
+       TEST_BF("bgt.w  2f")
+       TEST_BB("blt.w  2b")
+       TEST_BF_X("bpl.w        2f",0x1000)
+)
+
+       TEST_UNSUPPORTED("msr   cpsr, r0")
+       TEST_UNSUPPORTED("msr   cpsr_f, r1")
+       TEST_UNSUPPORTED("msr   spsr, r2")
+
+       TEST_UNSUPPORTED("cpsie.w       i")
+       TEST_UNSUPPORTED("cpsid.w       i")
+       TEST_UNSUPPORTED("cps   0x13")
+
+       TEST_SUPPORTED("yield.w")
+       TEST("sev.w")
+       TEST("nop.w")
+       TEST("wfi.w")
+       TEST_SUPPORTED("wfe.w")
+       TEST_UNSUPPORTED("dbg.w #0")
+
+       TEST_UNSUPPORTED("clrex")
+       TEST_UNSUPPORTED("dsb")
+       TEST_UNSUPPORTED("dmb")
+       TEST_UNSUPPORTED("isb")
+
+       TEST_UNSUPPORTED("bxj   r0")
+
+       TEST_UNSUPPORTED("subs  pc, lr, #4")
+
+       TEST("mrs       r0, cpsr")
+       TEST("mrs       r14, cpsr")
+       TEST_UNSUPPORTED(".short 0xf3ef,0x8d00  @ mrs   sp, spsr")
+       TEST_UNSUPPORTED(".short 0xf3ef,0x8f00  @ mrs   pc, spsr")
+       TEST_UNSUPPORTED("mrs   r0, spsr")
+       TEST_UNSUPPORTED("mrs   lr, spsr")
+
+       TEST_UNSUPPORTED(".short 0xf7f0,0x8000 @ smc #0")
+
+       TEST_UNSUPPORTED(".short 0xf7f0,0xa000 @ undefeined")
+
+       TEST_BF(  "b.w  2f")
+       TEST_BB(  "b.w  2b")
+       TEST_BF_X("b.w  2f", 0x1000)
+
+       TEST_BF(  "bl.w 2f")
+       TEST_BB(  "bl.w 2b")
+       TEST_BB_X("bl.w 2b", 0x1000)
+
+       TEST_X( "blx    __dummy_arm_subroutine",
+               ".arm                           \n\t"
+               ".align                         \n\t"
+               ".type __dummy_arm_subroutine, %%function \n\t"
+               "__dummy_arm_subroutine:        \n\t"
+               "mov    r0, pc                  \n\t"
+               "bx     lr                      \n\t"
+               ".thumb                         \n\t"
+       )
+       TEST(   "blx    __dummy_arm_subroutine")
+
+       TEST_GROUP("Store single data item")
+
+#define SINGLE_STORE(size)                                                     \
+       TEST_RP( "str"size"     r",0, VAL1,", [r",11,-1024,", #1024]")          \
+       TEST_RP( "str"size"     r",14,VAL2,", [r",1, -1024,", #1080]")          \
+       TEST_RP( "str"size"     r",0, VAL1,", [r",11,256,  ", #-120]")          \
+       TEST_RP( "str"size"     r",14,VAL2,", [r",1, 256,  ", #-128]")          \
+       TEST_RP( "str"size"     r",0, VAL1,", [r",11,24,  "], #120")            \
+       TEST_RP( "str"size"     r",14,VAL2,", [r",1, 24,  "], #128")            \
+       TEST_RP( "str"size"     r",0, VAL1,", [r",11,24,  "], #-120")           \
+       TEST_RP( "str"size"     r",14,VAL2,", [r",1, 24,  "], #-128")           \
+       TEST_RP( "str"size"     r",0, VAL1,", [r",11,24,   ", #120]!")          \
+       TEST_RP( "str"size"     r",14,VAL2,", [r",1, 24,   ", #128]!")          \
+       TEST_RP( "str"size"     r",0, VAL1,", [r",11,256,  ", #-120]!")         \
+       TEST_RP( "str"size"     r",14,VAL2,", [r",1, 256,  ", #-128]!")         \
+       TEST_RPR("str"size".w   r",0, VAL1,", [r",1, 0,", r",2, 4,"]")          \
+       TEST_RPR("str"size"     r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]")  \
+       TEST_R(  "str"size".w   r",7, VAL1,", [sp, #24]")                       \
+       TEST_RP( "str"size".w   r",0, VAL2,", [r",0,0, "]")                     \
+       TEST_UNSUPPORTED("str"size"t    r0, [r1, #4]")
+
+       SINGLE_STORE("b")
+       SINGLE_STORE("h")
+       SINGLE_STORE("")
+
+       TEST("str       sp, [sp]")
+       TEST_UNSUPPORTED(".short 0xf8cf,0xe000  @ str   r14, [pc]")
+       TEST_UNSUPPORTED(".short 0xf8ce,0xf000  @ str   pc, [r14]")
+
+       TEST_GROUP("Advanced SIMD element or structure load/store instructions")
+
+       TEST_UNSUPPORTED(".short 0xf900,0x0000")
+       TEST_UNSUPPORTED(".short 0xf92f,0xffff")
+       TEST_UNSUPPORTED(".short 0xf980,0x0000")
+       TEST_UNSUPPORTED(".short 0xf9ef,0xffff")
+
+       TEST_GROUP("Load single data item and memory hints")
+
+#define SINGLE_LOAD(size)                                              \
+       TEST_P( "ldr"size"      r0, [r",11,-1024, ", #1024]")           \
+       TEST_P( "ldr"size"      r14, [r",1, -1024,", #1080]")           \
+       TEST_P( "ldr"size"      r0, [r",11,256,   ", #-120]")           \
+       TEST_P( "ldr"size"      r14, [r",1, 256,  ", #-128]")           \
+       TEST_P( "ldr"size"      r0, [r",11,24,   "], #120")             \
+       TEST_P( "ldr"size"      r14, [r",1, 24,  "], #128")             \
+       TEST_P( "ldr"size"      r0, [r",11,24,   "], #-120")            \
+       TEST_P( "ldr"size"      r14, [r",1,24,   "], #-128")            \
+       TEST_P( "ldr"size"      r0, [r",11,24,    ", #120]!")           \
+       TEST_P( "ldr"size"      r14, [r",1, 24,   ", #128]!")           \
+       TEST_P( "ldr"size"      r0, [r",11,256,   ", #-120]!")          \
+       TEST_P( "ldr"size"      r14, [r",1, 256,  ", #-128]!")          \
+       TEST_PR("ldr"size".w    r0, [r",1, 0,", r",2, 4,"]")            \
+       TEST_PR("ldr"size"      r14, [r",10,0,", r",11,4,", lsl #1]")   \
+       TEST_X( "ldr"size".w    r0, 3f",                                \
+               ".align 3                               \n\t"           \
+               "3:     .word   "__stringify(VAL1))                     \
+       TEST_X( "ldr"size".w    r14, 3f",                               \
+               ".align 3                               \n\t"           \
+               "3:     .word   "__stringify(VAL2))                     \
+       TEST(   "ldr"size".w    r7, 3b")                                \
+       TEST(   "ldr"size".w    r7, [sp, #24]")                         \
+       TEST_P( "ldr"size".w    r0, [r",0,0, "]")                       \
+       TEST_UNSUPPORTED("ldr"size"t    r0, [r1, #4]")
+
+       SINGLE_LOAD("b")
+       SINGLE_LOAD("sb")
+       SINGLE_LOAD("h")
+       SINGLE_LOAD("sh")
+       SINGLE_LOAD("")
+
+       TEST_BF_P("ldr  pc, [r",14, 15*4,"]")
+       TEST_P(   "ldr  sp, [r",14, 13*4,"]")
+       TEST_BF_R("ldr  pc, [sp, r",14, 15*4,"]")
+       TEST_R(   "ldr  sp, [sp, r",14, 13*4,"]")
+       TEST_THUMB_TO_ARM_INTERWORK_P("ldr      pc, [r",0,0,", #15*4]")
+       TEST_SUPPORTED("ldr     sp, 99f")
+       TEST_SUPPORTED("ldr     pc, 99f")
+
+       TEST_UNSUPPORTED(".short 0xf854,0x700d  @ ldr   r7, [r4, sp]")
+       TEST_UNSUPPORTED(".short 0xf854,0x700f  @ ldr   r7, [r4, pc]")
+       TEST_UNSUPPORTED(".short 0xf814,0x700d  @ ldrb  r7, [r4, sp]")
+       TEST_UNSUPPORTED(".short 0xf814,0x700f  @ ldrb  r7, [r4, pc]")
+       TEST_UNSUPPORTED(".short 0xf89f,0xd004  @ ldrb  sp, 99f")
+       TEST_UNSUPPORTED(".short 0xf814,0xd008  @ ldrb  sp, [r4, r8]")
+       TEST_UNSUPPORTED(".short 0xf894,0xd000  @ ldrb  sp, [r4]")
+
+       TEST_UNSUPPORTED(".short 0xf860,0x0000") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xf9ff,0xffff") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xf950,0x0000") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xf95f,0xffff") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xf800,0x0800") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xf97f,0xfaff") /* Unallocated space */
+
+       TEST(   "pli    [pc, #4]")
+       TEST(   "pli    [pc, #-4]")
+       TEST(   "pld    [pc, #4]")
+       TEST(   "pld    [pc, #-4]")
+
+       TEST_P( "pld    [r",0,-1024,", #1024]")
+       TEST(   ".short 0xf8b0,0xf400   @ pldw  [r0, #1024]")
+       TEST_P( "pli    [r",4, 0b,", #1024]")
+       TEST_P( "pld    [r",7, 120,", #-120]")
+       TEST(   ".short 0xf837,0xfc78   @ pldw  [r7, #-120]")
+       TEST_P( "pli    [r",11,120,", #-120]")
+       TEST(   "pld    [sp, #0]")
+
+       TEST_PR("pld    [r",7, 24, ", r",0, 16,"]")
+       TEST_PR("pld    [r",8, 24, ", r",12,16,", lsl #3]")
+       TEST_SUPPORTED(".short 0xf837,0xf000    @ pldw  [r7, r0]")
+       TEST_SUPPORTED(".short 0xf838,0xf03c    @ pldw  [r8, r12, lsl #3]");
+       TEST_RR("pli    [r",12,0b,", r",0, 16,"]")
+       TEST_RR("pli    [r",0, 0b,", r",12,16,", lsl #3]")
+       TEST_R( "pld    [sp, r",1, 16,"]")
+       TEST_UNSUPPORTED(".short 0xf817,0xf00d  @pld    [r7, sp]")
+       TEST_UNSUPPORTED(".short 0xf817,0xf00f  @pld    [r7, pc]")
+
+       TEST_GROUP("Data-processing (register)")
+
+#define SHIFTS32(op)                                   \
+       TEST_RR(op"     r0,  r",1, VAL1,", r",2, 3, "") \
+       TEST_RR(op"     r14, r",12,VAL2,", r",11,10,"")
+
+       SHIFTS32("lsl")
+       SHIFTS32("lsls")
+       SHIFTS32("lsr")
+       SHIFTS32("lsrs")
+       SHIFTS32("asr")
+       SHIFTS32("asrs")
+       SHIFTS32("ror")
+       SHIFTS32("rors")
+
+       TEST_UNSUPPORTED(".short 0xfa01,0xff02  @ lsl   pc, r1, r2")
+       TEST_UNSUPPORTED(".short 0xfa01,0xfd02  @ lsl   sp, r1, r2")
+       TEST_UNSUPPORTED(".short 0xfa0f,0xf002  @ lsl   r0, pc, r2")
+       TEST_UNSUPPORTED(".short 0xfa0d,0xf002  @ lsl   r0, sp, r2")
+       TEST_UNSUPPORTED(".short 0xfa01,0xf00f  @ lsl   r0, r1, pc")
+       TEST_UNSUPPORTED(".short 0xfa01,0xf00d  @ lsl   r0, r1, sp")
+
+       TEST_RR(    "sxtah      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sxtah      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "sxth       r8, r",7,  HH1,"")
+
+       TEST_UNSUPPORTED(".short 0xfa0f,0xff87  @ sxth  pc, r7");
+       TEST_UNSUPPORTED(".short 0xfa0f,0xfd87  @ sxth  sp, r7");
+       TEST_UNSUPPORTED(".short 0xfa0f,0xf88f  @ sxth  r8, pc");
+       TEST_UNSUPPORTED(".short 0xfa0f,0xf88d  @ sxth  r8, sp");
+
+       TEST_RR(    "uxtah      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uxtah      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "uxth       r8, r",7,  HH1,"")
+
+       TEST_RR(    "sxtab16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sxtab16    r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "sxtb16     r8, r",7,  HH1,"")
+
+       TEST_RR(    "uxtab16    r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uxtab16    r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "uxtb16     r8, r",7,  HH1,"")
+
+       TEST_RR(    "sxtab      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "sxtab      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "sxtb       r8, r",7,  HH1,"")
+
+       TEST_RR(    "uxtab      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "uxtab      r14,r",12, HH2,", r",10,HH1,", ror #8")
+       TEST_R(     "uxtb       r8, r",7,  HH1,"")
+
+       TEST_UNSUPPORTED(".short 0xfa60,0x00f0")
+       TEST_UNSUPPORTED(".short 0xfa7f,0xffff")
+
+#define PARALLEL_ADD_SUB(op)                                   \
+       TEST_RR(  op"add16      r0, r",0,  HH1,", r",1, HH2,"") \
+       TEST_RR(  op"add16      r14, r",12,HH2,", r",10,HH1,"") \
+       TEST_RR(  op"asx        r0, r",0,  HH1,", r",1, HH2,"") \
+       TEST_RR(  op"asx        r14, r",12,HH2,", r",10,HH1,"") \
+       TEST_RR(  op"sax        r0, r",0,  HH1,", r",1, HH2,"") \
+       TEST_RR(  op"sax        r14, r",12,HH2,", r",10,HH1,"") \
+       TEST_RR(  op"sub16      r0, r",0,  HH1,", r",1, HH2,"") \
+       TEST_RR(  op"sub16      r14, r",12,HH2,", r",10,HH1,"") \
+       TEST_RR(  op"add8       r0, r",0,  HH1,", r",1, HH2,"") \
+       TEST_RR(  op"add8       r14, r",12,HH2,", r",10,HH1,"") \
+       TEST_RR(  op"sub8       r0, r",0,  HH1,", r",1, HH2,"") \
+       TEST_RR(  op"sub8       r14, r",12,HH2,", r",10,HH1,"")
+
+       TEST_GROUP("Parallel addition and subtraction, signed")
+
+       PARALLEL_ADD_SUB("s")
+       PARALLEL_ADD_SUB("q")
+       PARALLEL_ADD_SUB("sh")
+
+       TEST_GROUP("Parallel addition and subtraction, unsigned")
+
+       PARALLEL_ADD_SUB("u")
+       PARALLEL_ADD_SUB("uq")
+       PARALLEL_ADD_SUB("uh")
+
+       TEST_GROUP("Miscellaneous operations")
+
+       TEST_RR("qadd   r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR("qadd   lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_RR("qsub   r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR("qsub   lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_RR("qdadd  r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR("qdadd  lr, r",9, VAL2,", r",8, VAL1,"")
+       TEST_RR("qdsub  r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR("qdsub  lr, r",9, VAL2,", r",8, VAL1,"")
+
+       TEST_R("rev.w   r0, r",0,   VAL1,"")
+       TEST_R("rev     r14, r",12, VAL2,"")
+       TEST_R("rev16.w r0, r",0,   VAL1,"")
+       TEST_R("rev16   r14, r",12, VAL2,"")
+       TEST_R("rbit    r0, r",0,   VAL1,"")
+       TEST_R("rbit    r14, r",12, VAL2,"")
+       TEST_R("revsh.w r0, r",0,   VAL1,"")
+       TEST_R("revsh   r14, r",12, VAL2,"")
+
+       TEST_UNSUPPORTED(".short 0xfa9c,0xff8c  @ rev   pc, r12");
+       TEST_UNSUPPORTED(".short 0xfa9c,0xfd8c  @ rev   sp, r12");
+       TEST_UNSUPPORTED(".short 0xfa9f,0xfe8f  @ rev   r14, pc");
+       TEST_UNSUPPORTED(".short 0xfa9d,0xfe8d  @ rev   r14, sp");
+
+       TEST_RR("sel    r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR("sel    r14, r",12,VAL1,", r",10, VAL2,"")
+
+       TEST_R("clz     r0, r",0, 0x0,"")
+       TEST_R("clz     r7, r",14,0x1,"")
+       TEST_R("clz     lr, r",7, 0xffffffff,"")
+
+       TEST_UNSUPPORTED(".short 0xfa80,0xf030") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfaff,0xff7f") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfab0,0xf000") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfaff,0xff7f") /* Unallocated space */
+
+       TEST_GROUP("Multiply, multiply accumulate, and absolute difference operations")
+
+       TEST_RR(    "mul        r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "mul        r7, r",8, VAL2,", r",9, VAL2,"")
+       TEST_UNSUPPORTED(".short 0xfb08,0xff09  @ mul   pc, r8, r9")
+       TEST_UNSUPPORTED(".short 0xfb08,0xfd09  @ mul   sp, r8, r9")
+       TEST_UNSUPPORTED(".short 0xfb0f,0xf709  @ mul   r7, pc, r9")
+       TEST_UNSUPPORTED(".short 0xfb0d,0xf709  @ mul   r7, sp, r9")
+       TEST_UNSUPPORTED(".short 0xfb08,0xf70f  @ mul   r7, r8, pc")
+       TEST_UNSUPPORTED(".short 0xfb08,0xf70d  @ mul   r7, r8, sp")
+
+       TEST_RRR(   "mla        r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "mla        r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_UNSUPPORTED(".short 0xfb08,0xaf09  @ mla   pc, r8, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb08,0xad09  @ mla   sp, r8, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb0f,0xa709  @ mla   r7, pc, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb0d,0xa709  @ mla   r7, sp, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb08,0xa70f  @ mla   r7, r8, pc, r10");
+       TEST_UNSUPPORTED(".short 0xfb08,0xa70d  @ mla   r7, r8, sp, r10");
+       TEST_UNSUPPORTED(".short 0xfb08,0xd709  @ mla   r7, r8, r9, sp");
+
+       TEST_RRR(   "mls        r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "mls        r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+
+       TEST_RRR(   "smlabb     r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "smlabb     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RRR(   "smlatb     r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "smlatb     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RRR(   "smlabt     r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "smlabt     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RRR(   "smlatt     r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "smlatt     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(    "smulbb     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulbb     r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_RR(    "smultb     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smultb     r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_RR(    "smulbt     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulbt     r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_RR(    "smultt     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smultt     r7, r",8, VAL3,", r",9, VAL1,"")
+
+       TEST_RRR(   "smlad      r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smlad      r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_RRR(   "smladx     r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smladx     r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_RR(    "smuad      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "smuad      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_RR(    "smuadx     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "smuadx     r14, r",12,HH2,", r",10,HH1,"")
+
+       TEST_RRR(   "smlawb     r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "smlawb     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RRR(   "smlawt     r0, r",1, VAL1,", r",2, VAL2,", r",3,  VAL3,"")
+       TEST_RRR(   "smlawt     r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"")
+       TEST_RR(    "smulwb     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulwb     r7, r",8, VAL3,", r",9, VAL1,"")
+       TEST_RR(    "smulwt     r0, r",1, VAL1,", r",2, VAL2,"")
+       TEST_RR(    "smulwt     r7, r",8, VAL3,", r",9, VAL1,"")
+
+       TEST_RRR(   "smlsd      r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smlsd      r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_RRR(   "smlsdx     r0, r",0,  HH1,", r",1, HH2,", r",2, VAL1,"")
+       TEST_RRR(   "smlsdx     r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"")
+       TEST_RR(    "smusd      r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "smusd      r14, r",12,HH2,", r",10,HH1,"")
+       TEST_RR(    "smusdx     r0, r",0,  HH1,", r",1, HH2,"")
+       TEST_RR(    "smusdx     r14, r",12,HH2,", r",10,HH1,"")
+
+       TEST_RRR(   "smmla      r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(   "smmla      r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_RRR(   "smmlar     r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(   "smmlar     r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_RR(    "smmul      r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(    "smmul      r14, r",12,VAL2,", r",10,VAL1,"")
+       TEST_RR(    "smmulr     r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(    "smmulr     r14, r",12,VAL2,", r",10,VAL1,"")
+
+       TEST_RRR(   "smmls      r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(   "smmls      r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+       TEST_RRR(   "smmlsr     r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL1,"")
+       TEST_RRR(   "smmlsr     r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"")
+
+       TEST_RRR(   "usada8     r0, r",0,  VAL1,", r",1, VAL2,", r",2, VAL3,"")
+       TEST_RRR(   "usada8     r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"")
+       TEST_RR(    "usad8      r0, r",0,  VAL1,", r",1, VAL2,"")
+       TEST_RR(    "usad8      r14, r",12,VAL2,", r",10,VAL1,"")
+
+       TEST_UNSUPPORTED(".short 0xfb00,0xf010") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfb0f,0xff1f") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfb70,0xf010") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfb7f,0xff1f") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfb70,0x0010") /* Unallocated space */
+       TEST_UNSUPPORTED(".short 0xfb7f,0xff1f") /* Unallocated space */
+
+       TEST_GROUP("Long multiply, long multiply accumulate, and divide")
+
+       TEST_RR(   "smull       r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(   "smull       r7, r8, r",9, VAL2,", r",10, VAL1,"")
+       TEST_UNSUPPORTED(".short 0xfb89,0xf80a  @ smull pc, r8, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb89,0xd80a  @ smull sp, r8, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb89,0x7f0a  @ smull r7, pc, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb89,0x7d0a  @ smull r7, sp, r9, r10");
+       TEST_UNSUPPORTED(".short 0xfb8f,0x780a  @ smull r7, r8, pc, r10");
+       TEST_UNSUPPORTED(".short 0xfb8d,0x780a  @ smull r7, r8, sp, r10");
+       TEST_UNSUPPORTED(".short 0xfb89,0x780f  @ smull r7, r8, r9, pc");
+       TEST_UNSUPPORTED(".short 0xfb89,0x780d  @ smull r7, r8, r9, sp");
+
+       TEST_RR(   "umull       r0, r1, r",2, VAL1,", r",3, VAL2,"")
+       TEST_RR(   "umull       r7, r8, r",9, VAL2,", r",10, VAL1,"")
+
+       TEST_RRRR( "smlal       r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "smlal       r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+
+       TEST_RRRR( "smlalbb     r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "smlalbb     r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRRR( "smlalbt     r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "smlalbt     r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRRR( "smlaltb     r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "smlaltb     r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRRR( "smlaltt     r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "smlaltt     r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+
+       TEST_RRRR( "smlald      r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2)
+       TEST_RRRR( "smlald      r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1)
+       TEST_RRRR( "smlaldx     r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2)
+       TEST_RRRR( "smlaldx     r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1)
+
+       TEST_RRRR( "smlsld      r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2)
+       TEST_RRRR( "smlsld      r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1)
+       TEST_RRRR( "smlsldx     r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2)
+       TEST_RRRR( "smlsldx     r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1)
+
+       TEST_RRRR( "umlal       r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "umlal       r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+       TEST_RRRR( "umaal       r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4)
+       TEST_RRRR( "umaal       r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3)
+
+       TEST_GROUP("Coprocessor instructions")
+
+       TEST_UNSUPPORTED(".short 0xfc00,0x0000")
+       TEST_UNSUPPORTED(".short 0xffff,0xffff")
+
+       TEST_GROUP("Testing instructions in IT blocks")
+
+       TEST_ITBLOCK("sub.w     r0, r0")
+
+       verbose("\n");
+}
+
diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c
new file mode 100644 (file)
index 0000000..e17cdd6
--- /dev/null
@@ -0,0 +1,1748 @@
+/*
+ * arch/arm/kernel/kprobes-test.c
+ *
+ * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
+ *
+ * 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 file contains test code for ARM kprobes.
+ *
+ * The top level function run_all_tests() executes tests for all of the
+ * supported instruction sets: ARM, 16-bit Thumb, and 32-bit Thumb. These tests
+ * fall into two categories; run_api_tests() checks basic functionality of the
+ * kprobes API, and run_test_cases() is a comprehensive test for kprobes
+ * instruction decoding and simulation.
+ *
+ * run_test_cases() first checks the kprobes decoding table for self consistency
+ * (using table_test()) then executes a series of test cases for each of the CPU
+ * instruction forms. coverage_start() and coverage_end() are used to verify
+ * that these test cases cover all of the possible combinations of instructions
+ * described by the kprobes decoding tables.
+ *
+ * The individual test cases are in kprobes-test-arm.c and kprobes-test-thumb.c
+ * which use the macros defined in kprobes-test.h. The rest of this
+ * documentation will describe the operation of the framework used by these
+ * test cases.
+ */
+
+/*
+ * TESTING METHODOLOGY
+ * -------------------
+ *
+ * The methodology used to test an ARM instruction 'test_insn' is to use
+ * inline assembler like:
+ *
+ * test_before: nop
+ * test_case:  test_insn
+ * test_after: nop
+ *
+ * When the test case is run a kprobe is placed of each nop. The
+ * post-handler of the test_before probe is used to modify the saved CPU
+ * register context to that which we require for the test case. The
+ * pre-handler of the of the test_after probe saves a copy of the CPU
+ * register context. In this way we can execute test_insn with a specific
+ * register context and see the results afterwards.
+ *
+ * To actually test the kprobes instruction emulation we perform the above
+ * step a second time but with an additional kprobe on the test_case
+ * instruction itself. If the emulation is accurate then the results seen
+ * by the test_after probe will be identical to the first run which didn't
+ * have a probe on test_case.
+ *
+ * Each test case is run several times with a variety of variations in the
+ * flags value of stored in CPSR, and for Thumb code, different ITState.
+ *
+ * For instructions which can modify PC, a second test_after probe is used
+ * like this:
+ *
+ * test_before: nop
+ * test_case:  test_insn
+ * test_after: nop
+ *             b test_done
+ * test_after2: nop
+ * test_done:
+ *
+ * The test case is constructed such that test_insn branches to
+ * test_after2, or, if testing a conditional instruction, it may just
+ * continue to test_after. The probes inserted at both locations let us
+ * determine which happened. A similar approach is used for testing
+ * backwards branches...
+ *
+ *             b test_before
+ *             b test_done  @ helps to cope with off by 1 branches
+ * test_after2: nop
+ *             b test_done
+ * test_before: nop
+ * test_case:  test_insn
+ * test_after: nop
+ * test_done:
+ *
+ * The macros used to generate the assembler instructions describe above
+ * are TEST_INSTRUCTION, TEST_BRANCH_F (branch forwards) and TEST_BRANCH_B
+ * (branch backwards). In these, the local variables numbered 1, 50, 2 and
+ * 99 represent: test_before, test_case, test_after2 and test_done.
+ *
+ * FRAMEWORK
+ * ---------
+ *
+ * Each test case is wrapped between the pair of macros TESTCASE_START and
+ * TESTCASE_END. As well as performing the inline assembler boilerplate,
+ * these call out to the kprobes_test_case_start() and
+ * kprobes_test_case_end() functions which drive the execution of the test
+ * case. The specific arguments to use for each test case are stored as
+ * inline data constructed using the various TEST_ARG_* macros. Putting
+ * this all together, a simple test case may look like:
+ *
+ *     TESTCASE_START("Testing mov r0, r7")
+ *     TEST_ARG_REG(7, 0x12345678) // Set r7=0x12345678
+ *     TEST_ARG_END("")
+ *     TEST_INSTRUCTION("mov r0, r7")
+ *     TESTCASE_END
+ *
+ * Note, in practice the single convenience macro TEST_R would be used for this
+ * instead.
+ *
+ * The above would expand to assembler looking something like:
+ *
+ *     @ TESTCASE_START
+ *     bl      __kprobes_test_case_start
+ *     @ start of inline data...
+ *     .ascii "mov r0, r7"     @ text title for test case
+ *     .byte   0
+ *     .align  2
+ *
+ *     @ TEST_ARG_REG
+ *     .byte   ARG_TYPE_REG
+ *     .byte   7
+ *     .short  0
+ *     .word   0x1234567
+ *
+ *     @ TEST_ARG_END
+ *     .byte   ARG_TYPE_END
+ *     .byte   TEST_ISA        @ flags, including ISA being tested
+ *     .short  50f-0f          @ offset of 'test_before'
+ *     .short  2f-0f           @ offset of 'test_after2' (if relevent)
+ *     .short  99f-0f          @ offset of 'test_done'
+ *     @ start of test case code...
+ *     0:
+ *     .code   TEST_ISA        @ switch to ISA being tested
+ *
+ *     @ TEST_INSTRUCTION
+ *     50:     nop             @ location for 'test_before' probe
+ *     1:      mov r0, r7      @ the test case instruction 'test_insn'
+ *             nop             @ location for 'test_after' probe
+ *
+ *     // TESTCASE_END
+ *     2:
+ *     99:     bl __kprobes_test_case_end_##TEST_ISA
+ *     .code   NONMAL_ISA
+ *
+ * When the above is execute the following happens...
+ *
+ * __kprobes_test_case_start() is an assembler wrapper which sets up space
+ * for a stack buffer and calls the C function kprobes_test_case_start().
+ * This C function will do some initial processing of the inline data and
+ * setup some global state. It then inserts the test_before and test_after
+ * kprobes and returns a value which causes the assembler wrapper to jump
+ * to the start of the test case code, (local label '0').
+ *
+ * When the test case code executes, the test_before probe will be hit and
+ * test_before_post_handler will call setup_test_context(). This fills the
+ * stack buffer and CPU registers with a test pattern and then processes
+ * the test case arguments. In our example there is one TEST_ARG_REG which
+ * indicates that R7 should be loaded with the value 0x12345678.
+ *
+ * When the test_before probe ends, the test case continues and executes
+ * the "mov r0, r7" instruction. It then hits the test_after probe and the
+ * pre-handler for this (test_after_pre_handler) will save a copy of the
+ * CPU register context. This should now have R0 holding the same value as
+ * R7.
+ *
+ * Finally we get to the call to __kprobes_test_case_end_{32,16}. This is
+ * an assembler wrapper which switches back to the ISA used by the test
+ * code and calls the C function kprobes_test_case_end().
+ *
+ * For each run through the test case, test_case_run_count is incremented
+ * by one. For even runs, kprobes_test_case_end() saves a copy of the
+ * register and stack buffer contents from the test case just run. It then
+ * inserts a kprobe on the test case instruction 'test_insn' and returns a
+ * value to cause the test case code to be re-run.
+ *
+ * For odd numbered runs, kprobes_test_case_end() compares the register and
+ * stack buffer contents to those that were saved on the previous even
+ * numbered run (the one without the kprobe on test_insn). These should be
+ * the same if the kprobe instruction simulation routine is correct.
+ *
+ * The pair of test case runs is repeated with different combinations of
+ * flag values in CPSR and, for Thumb, different ITState. This is
+ * controlled by test_context_cpsr().
+ *
+ * BUILDING TEST CASES
+ * -------------------
+ *
+ *
+ * As an aid to building test cases, the stack buffer is initialised with
+ * some special values:
+ *
+ *   [SP+13*4] Contains SP+120. This can be used to test instructions
+ *             which load a value into SP.
+ *
+ *   [SP+15*4] When testing branching instructions using TEST_BRANCH_{F,B},
+ *             this holds the target address of the branch, 'test_after2'.
+ *             This can be used to test instructions which load a PC value
+ *             from memory.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kprobes.h>
+
+#include "kprobes.h"
+#include "kprobes-test.h"
+
+
+#define BENCHMARKING   1
+
+
+/*
+ * Test basic API
+ */
+
+static bool test_regs_ok;
+static int test_func_instance;
+static int pre_handler_called;
+static int post_handler_called;
+static int jprobe_func_called;
+static int kretprobe_handler_called;
+
+#define FUNC_ARG1 0x12345678
+#define FUNC_ARG2 0xabcdef
+
+
+#ifndef CONFIG_THUMB2_KERNEL
+
+long arm_func(long r0, long r1);
+
+static void __used __naked __arm_kprobes_test_func(void)
+{
+       __asm__ __volatile__ (
+               ".arm                                   \n\t"
+               ".type arm_func, %%function             \n\t"
+               "arm_func:                              \n\t"
+               "adds   r0, r0, r1                      \n\t"
+               "bx     lr                              \n\t"
+               ".code "NORMAL_ISA       /* Back to Thumb if necessary */
+               : : : "r0", "r1", "cc"
+       );
+}
+
+#else /* CONFIG_THUMB2_KERNEL */
+
+long thumb16_func(long r0, long r1);
+long thumb32even_func(long r0, long r1);
+long thumb32odd_func(long r0, long r1);
+
+static void __used __naked __thumb_kprobes_test_funcs(void)
+{
+       __asm__ __volatile__ (
+               ".type thumb16_func, %%function         \n\t"
+               "thumb16_func:                          \n\t"
+               "adds.n r0, r0, r1                      \n\t"
+               "bx     lr                              \n\t"
+
+               ".align                                 \n\t"
+               ".type thumb32even_func, %%function     \n\t"
+               "thumb32even_func:                      \n\t"
+               "adds.w r0, r0, r1                      \n\t"
+               "bx     lr                              \n\t"
+
+               ".align                                 \n\t"
+               "nop.n                                  \n\t"
+               ".type thumb32odd_func, %%function      \n\t"
+               "thumb32odd_func:                       \n\t"
+               "adds.w r0, r0, r1                      \n\t"
+               "bx     lr                              \n\t"
+
+               : : : "r0", "r1", "cc"
+       );
+}
+
+#endif /* CONFIG_THUMB2_KERNEL */
+
+
+static int call_test_func(long (*func)(long, long), bool check_test_regs)
+{
+       long ret;
+
+       ++test_func_instance;
+       test_regs_ok = false;
+
+       ret = (*func)(FUNC_ARG1, FUNC_ARG2);
+       if (ret != FUNC_ARG1 + FUNC_ARG2) {
+               pr_err("FAIL: call_test_func: func returned %lx\n", ret);
+               return false;
+       }
+
+       if (check_test_regs && !test_regs_ok) {
+               pr_err("FAIL: test regs not OK\n");
+               return false;
+       }
+
+       return true;
+}
+
+static int __kprobes pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       pre_handler_called = test_func_instance;
+       if (regs->ARM_r0 == FUNC_ARG1 && regs->ARM_r1 == FUNC_ARG2)
+               test_regs_ok = true;
+       return 0;
+}
+
+static void __kprobes post_handler(struct kprobe *p, struct pt_regs *regs,
+                               unsigned long flags)
+{
+       post_handler_called = test_func_instance;
+       if (regs->ARM_r0 != FUNC_ARG1 + FUNC_ARG2 || regs->ARM_r1 != FUNC_ARG2)
+               test_regs_ok = false;
+}
+
+static struct kprobe the_kprobe = {
+       .addr           = 0,
+       .pre_handler    = pre_handler,
+       .post_handler   = post_handler
+};
+
+static int test_kprobe(long (*func)(long, long))
+{
+       int ret;
+
+       the_kprobe.addr = (kprobe_opcode_t *)func;
+       ret = register_kprobe(&the_kprobe);
+       if (ret < 0) {
+               pr_err("FAIL: register_kprobe failed with %d\n", ret);
+               return ret;
+       }
+
+       ret = call_test_func(func, true);
+
+       unregister_kprobe(&the_kprobe);
+       the_kprobe.flags = 0; /* Clear disable flag to allow reuse */
+
+       if (!ret)
+               return -EINVAL;
+       if (pre_handler_called != test_func_instance) {
+               pr_err("FAIL: kprobe pre_handler not called\n");
+               return -EINVAL;
+       }
+       if (post_handler_called != test_func_instance) {
+               pr_err("FAIL: kprobe post_handler not called\n");
+               return -EINVAL;
+       }
+       if (!call_test_func(func, false))
+               return -EINVAL;
+       if (pre_handler_called == test_func_instance ||
+                               post_handler_called == test_func_instance) {
+               pr_err("FAIL: probe called after unregistering\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void __kprobes jprobe_func(long r0, long r1)
+{
+       jprobe_func_called = test_func_instance;
+       if (r0 == FUNC_ARG1 && r1 == FUNC_ARG2)
+               test_regs_ok = true;
+       jprobe_return();
+}
+
+static struct jprobe the_jprobe = {
+       .entry          = jprobe_func,
+};
+
+static int test_jprobe(long (*func)(long, long))
+{
+       int ret;
+
+       the_jprobe.kp.addr = (kprobe_opcode_t *)func;
+       ret = register_jprobe(&the_jprobe);
+       if (ret < 0) {
+               pr_err("FAIL: register_jprobe failed with %d\n", ret);
+               return ret;
+       }
+
+       ret = call_test_func(func, true);
+
+       unregister_jprobe(&the_jprobe);
+       the_jprobe.kp.flags = 0; /* Clear disable flag to allow reuse */
+
+       if (!ret)
+               return -EINVAL;
+       if (jprobe_func_called != test_func_instance) {
+               pr_err("FAIL: jprobe handler function not called\n");
+               return -EINVAL;
+       }
+       if (!call_test_func(func, false))
+               return -EINVAL;
+       if (jprobe_func_called == test_func_instance) {
+               pr_err("FAIL: probe called after unregistering\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __kprobes
+kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+       kretprobe_handler_called = test_func_instance;
+       if (regs_return_value(regs) == FUNC_ARG1 + FUNC_ARG2)
+               test_regs_ok = true;
+       return 0;
+}
+
+static struct kretprobe the_kretprobe = {
+       .handler        = kretprobe_handler,
+};
+
+static int test_kretprobe(long (*func)(long, long))
+{
+       int ret;
+
+       the_kretprobe.kp.addr = (kprobe_opcode_t *)func;
+       ret = register_kretprobe(&the_kretprobe);
+       if (ret < 0) {
+               pr_err("FAIL: register_kretprobe failed with %d\n", ret);
+               return ret;
+       }
+
+       ret = call_test_func(func, true);
+
+       unregister_kretprobe(&the_kretprobe);
+       the_kretprobe.kp.flags = 0; /* Clear disable flag to allow reuse */
+
+       if (!ret)
+               return -EINVAL;
+       if (kretprobe_handler_called != test_func_instance) {
+               pr_err("FAIL: kretprobe handler not called\n");
+               return -EINVAL;
+       }
+       if (!call_test_func(func, false))
+               return -EINVAL;
+       if (jprobe_func_called == test_func_instance) {
+               pr_err("FAIL: kretprobe called after unregistering\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int run_api_tests(long (*func)(long, long))
+{
+       int ret;
+
+       pr_info("    kprobe\n");
+       ret = test_kprobe(func);
+       if (ret < 0)
+               return ret;
+
+       pr_info("    jprobe\n");
+       ret = test_jprobe(func);
+       if (ret < 0)
+               return ret;
+
+       pr_info("    kretprobe\n");
+       ret = test_kretprobe(func);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+
+/*
+ * Benchmarking
+ */
+
+#if BENCHMARKING
+
+static void __naked benchmark_nop(void)
+{
+       __asm__ __volatile__ (
+               "nop            \n\t"
+               "bx     lr"
+       );
+}
+
+#ifdef CONFIG_THUMB2_KERNEL
+#define wide ".w"
+#else
+#define wide
+#endif
+
+static void __naked benchmark_pushpop1(void)
+{
+       __asm__ __volatile__ (
+               "stmdb"wide"    sp!, {r3-r11,lr}  \n\t"
+               "ldmia"wide"    sp!, {r3-r11,pc}"
+       );
+}
+
+static void __naked benchmark_pushpop2(void)
+{
+       __asm__ __volatile__ (
+               "stmdb"wide"    sp!, {r0-r8,lr}  \n\t"
+               "ldmia"wide"    sp!, {r0-r8,pc}"
+       );
+}
+
+static void __naked benchmark_pushpop3(void)
+{
+       __asm__ __volatile__ (
+               "stmdb"wide"    sp!, {r4,lr}  \n\t"
+               "ldmia"wide"    sp!, {r4,pc}"
+       );
+}
+
+static void __naked benchmark_pushpop4(void)
+{
+       __asm__ __volatile__ (
+               "stmdb"wide"    sp!, {r0,lr}  \n\t"
+               "ldmia"wide"    sp!, {r0,pc}"
+       );
+}
+
+
+#ifdef CONFIG_THUMB2_KERNEL
+
+static void __naked benchmark_pushpop_thumb(void)
+{
+       __asm__ __volatile__ (
+               "push.n {r0-r7,lr}  \n\t"
+               "pop.n  {r0-r7,pc}"
+       );
+}
+
+#endif
+
+static int __kprobes
+benchmark_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       return 0;
+}
+
+static int benchmark(void(*fn)(void))
+{
+       unsigned n, i, t, t0;
+
+       for (n = 1000; ; n *= 2) {
+               t0 = sched_clock();
+               for (i = n; i > 0; --i)
+                       fn();
+               t = sched_clock() - t0;
+               if (t >= 250000000)
+                       break; /* Stop once we took more than 0.25 seconds */
+       }
+       return t / n; /* Time for one iteration in nanoseconds */
+};
+
+static int kprobe_benchmark(void(*fn)(void), unsigned offset)
+{
+       struct kprobe k = {
+               .addr           = (kprobe_opcode_t *)((uintptr_t)fn + offset),
+               .pre_handler    = benchmark_pre_handler,
+       };
+
+       int ret = register_kprobe(&k);
+       if (ret < 0) {
+               pr_err("FAIL: register_kprobe failed with %d\n", ret);
+               return ret;
+       }
+
+       ret = benchmark(fn);
+
+       unregister_kprobe(&k);
+       return ret;
+};
+
+struct benchmarks {
+       void            (*fn)(void);
+       unsigned        offset;
+       const char      *title;
+};
+
+static int run_benchmarks(void)
+{
+       int ret;
+       struct benchmarks list[] = {
+               {&benchmark_nop, 0, "nop"},
+               /*
+                * benchmark_pushpop{1,3} will have the optimised
+                * instruction emulation, whilst benchmark_pushpop{2,4} will
+                * be the equivalent unoptimised instructions.
+                */
+               {&benchmark_pushpop1, 0, "stmdb sp!, {r3-r11,lr}"},
+               {&benchmark_pushpop1, 4, "ldmia sp!, {r3-r11,pc}"},
+               {&benchmark_pushpop2, 0, "stmdb sp!, {r0-r8,lr}"},
+               {&benchmark_pushpop2, 4, "ldmia sp!, {r0-r8,pc}"},
+               {&benchmark_pushpop3, 0, "stmdb sp!, {r4,lr}"},
+               {&benchmark_pushpop3, 4, "ldmia sp!, {r4,pc}"},
+               {&benchmark_pushpop4, 0, "stmdb sp!, {r0,lr}"},
+               {&benchmark_pushpop4, 4, "ldmia sp!, {r0,pc}"},
+#ifdef CONFIG_THUMB2_KERNEL
+               {&benchmark_pushpop_thumb, 0, "push.n   {r0-r7,lr}"},
+               {&benchmark_pushpop_thumb, 2, "pop.n    {r0-r7,pc}"},
+#endif
+               {0}
+       };
+
+       struct benchmarks *b;
+       for (b = list; b->fn; ++b) {
+               ret = kprobe_benchmark(b->fn, b->offset);
+               if (ret < 0)
+                       return ret;
+               pr_info("    %dns for kprobe %s\n", ret, b->title);
+       }
+
+       pr_info("\n");
+       return 0;
+}
+
+#endif /* BENCHMARKING */
+
+
+/*
+ * Decoding table self-consistency tests
+ */
+
+static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
+       [DECODE_TYPE_TABLE]     = sizeof(struct decode_table),
+       [DECODE_TYPE_CUSTOM]    = sizeof(struct decode_custom),
+       [DECODE_TYPE_SIMULATE]  = sizeof(struct decode_simulate),
+       [DECODE_TYPE_EMULATE]   = sizeof(struct decode_emulate),
+       [DECODE_TYPE_OR]        = sizeof(struct decode_or),
+       [DECODE_TYPE_REJECT]    = sizeof(struct decode_reject)
+};
+
+static int table_iter(const union decode_item *table,
+                       int (*fn)(const struct decode_header *, void *),
+                       void *args)
+{
+       const struct decode_header *h = (struct decode_header *)table;
+       int result;
+
+       for (;;) {
+               enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+
+               if (type == DECODE_TYPE_END)
+                       return 0;
+
+               result = fn(h, args);
+               if (result)
+                       return result;
+
+               h = (struct decode_header *)
+                       ((uintptr_t)h + decode_struct_sizes[type]);
+
+       }
+}
+
+static int table_test_fail(const struct decode_header *h, const char* message)
+{
+
+       pr_err("FAIL: kprobes test failure \"%s\" (mask %08x, value %08x)\n",
+                                       message, h->mask.bits, h->value.bits);
+       return -EINVAL;
+}
+
+struct table_test_args {
+       const union decode_item *root_table;
+       u32                     parent_mask;
+       u32                     parent_value;
+};
+
+static int table_test_fn(const struct decode_header *h, void *args)
+{
+       struct table_test_args *a = (struct table_test_args *)args;
+       enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+
+       if (h->value.bits & ~h->mask.bits)
+               return table_test_fail(h, "Match value has bits not in mask");
+
+       if ((h->mask.bits & a->parent_mask) != a->parent_mask)
+               return table_test_fail(h, "Mask has bits not in parent mask");
+
+       if ((h->value.bits ^ a->parent_value) & a->parent_mask)
+               return table_test_fail(h, "Value is inconsistent with parent");
+
+       if (type == DECODE_TYPE_TABLE) {
+               struct decode_table *d = (struct decode_table *)h;
+               struct table_test_args args2 = *a;
+               args2.parent_mask = h->mask.bits;
+               args2.parent_value = h->value.bits;
+               return table_iter(d->table.table, table_test_fn, &args2);
+       }
+
+       return 0;
+}
+
+static int table_test(const union decode_item *table)
+{
+       struct table_test_args args = {
+               .root_table     = table,
+               .parent_mask    = 0,
+               .parent_value   = 0
+       };
+       return table_iter(args.root_table, table_test_fn, &args);
+}
+
+
+/*
+ * Decoding table test coverage analysis
+ *
+ * coverage_start() builds a coverage_table which contains a list of
+ * coverage_entry's to match each entry in the specified kprobes instruction
+ * decoding table.
+ *
+ * When test cases are run, coverage_add() is called to process each case.
+ * This looks up the corresponding entry in the coverage_table and sets it as
+ * being matched, as well as clearing the regs flag appropriate for the test.
+ *
+ * After all test cases have been run, coverage_end() is called to check that
+ * all entries in coverage_table have been matched and that all regs flags are
+ * cleared. I.e. that all possible combinations of instructions described by
+ * the kprobes decoding tables have had a test case executed for them.
+ */
+
+bool coverage_fail;
+
+#define MAX_COVERAGE_ENTRIES 256
+
+struct coverage_entry {
+       const struct decode_header      *header;
+       unsigned                        regs;
+       unsigned                        nesting;
+       char                            matched;
+};
+
+struct coverage_table {
+       struct coverage_entry   *base;
+       unsigned                num_entries;
+       unsigned                nesting;
+};
+
+struct coverage_table coverage;
+
+#define COVERAGE_ANY_REG       (1<<0)
+#define COVERAGE_SP            (1<<1)
+#define COVERAGE_PC            (1<<2)
+#define COVERAGE_PCWB          (1<<3)
+
+static const char coverage_register_lookup[16] = {
+       [REG_TYPE_ANY]          = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC,
+       [REG_TYPE_SAMEAS16]     = COVERAGE_ANY_REG,
+       [REG_TYPE_SP]           = COVERAGE_SP,
+       [REG_TYPE_PC]           = COVERAGE_PC,
+       [REG_TYPE_NOSP]         = COVERAGE_ANY_REG | COVERAGE_SP,
+       [REG_TYPE_NOSPPC]       = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC,
+       [REG_TYPE_NOPC]         = COVERAGE_ANY_REG | COVERAGE_PC,
+       [REG_TYPE_NOPCWB]       = COVERAGE_ANY_REG | COVERAGE_PC | COVERAGE_PCWB,
+       [REG_TYPE_NOPCX]        = COVERAGE_ANY_REG,
+       [REG_TYPE_NOSPPCX]      = COVERAGE_ANY_REG | COVERAGE_SP,
+};
+
+unsigned coverage_start_registers(const struct decode_header *h)
+{
+       unsigned regs = 0;
+       int i;
+       for (i = 0; i < 20; i += 4) {
+               int r = (h->type_regs.bits >> (DECODE_TYPE_BITS + i)) & 0xf;
+               regs |= coverage_register_lookup[r] << i;
+       }
+       return regs;
+}
+
+static int coverage_start_fn(const struct decode_header *h, void *args)
+{
+       struct coverage_table *coverage = (struct coverage_table *)args;
+       enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+       struct coverage_entry *entry = coverage->base + coverage->num_entries;
+
+       if (coverage->num_entries == MAX_COVERAGE_ENTRIES - 1) {
+               pr_err("FAIL: Out of space for test coverage data");
+               return -ENOMEM;
+       }
+
+       ++coverage->num_entries;
+
+       entry->header = h;
+       entry->regs = coverage_start_registers(h);
+       entry->nesting = coverage->nesting;
+       entry->matched = false;
+
+       if (type == DECODE_TYPE_TABLE) {
+               struct decode_table *d = (struct decode_table *)h;
+               int ret;
+               ++coverage->nesting;
+               ret = table_iter(d->table.table, coverage_start_fn, coverage);
+               --coverage->nesting;
+               return ret;
+       }
+
+       return 0;
+}
+
+static int coverage_start(const union decode_item *table)
+{
+       coverage.base = kmalloc(MAX_COVERAGE_ENTRIES *
+                               sizeof(struct coverage_entry), GFP_KERNEL);
+       coverage.num_entries = 0;
+       coverage.nesting = 0;
+       return table_iter(table, coverage_start_fn, &coverage);
+}
+
+static void
+coverage_add_registers(struct coverage_entry *entry, kprobe_opcode_t insn)
+{
+       int regs = entry->header->type_regs.bits >> DECODE_TYPE_BITS;
+       int i;
+       for (i = 0; i < 20; i += 4) {
+               enum decode_reg_type reg_type = (regs >> i) & 0xf;
+               int reg = (insn >> i) & 0xf;
+               int flag;
+
+               if (!reg_type)
+                       continue;
+
+               if (reg == 13)
+                       flag = COVERAGE_SP;
+               else if (reg == 15)
+                       flag = COVERAGE_PC;
+               else
+                       flag = COVERAGE_ANY_REG;
+               entry->regs &= ~(flag << i);
+
+               switch (reg_type) {
+
+               case REG_TYPE_NONE:
+               case REG_TYPE_ANY:
+               case REG_TYPE_SAMEAS16:
+                       break;
+
+               case REG_TYPE_SP:
+                       if (reg != 13)
+                               return;
+                       break;
+
+               case REG_TYPE_PC:
+                       if (reg != 15)
+                               return;
+                       break;
+
+               case REG_TYPE_NOSP:
+                       if (reg == 13)
+                               return;
+                       break;
+
+               case REG_TYPE_NOSPPC:
+               case REG_TYPE_NOSPPCX:
+                       if (reg == 13 || reg == 15)
+                               return;
+                       break;
+
+               case REG_TYPE_NOPCWB:
+                       if (!is_writeback(insn))
+                               break;
+                       if (reg == 15) {
+                               entry->regs &= ~(COVERAGE_PCWB << i);
+                               return;
+                       }
+                       break;
+
+               case REG_TYPE_NOPC:
+               case REG_TYPE_NOPCX:
+                       if (reg == 15)
+                               return;
+                       break;
+               }
+
+       }
+}
+
+static void coverage_add(kprobe_opcode_t insn)
+{
+       struct coverage_entry *entry = coverage.base;
+       struct coverage_entry *end = coverage.base + coverage.num_entries;
+       bool matched = false;
+       unsigned nesting = 0;
+
+       for (; entry < end; ++entry) {
+               const struct decode_header *h = entry->header;
+               enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
+
+               if (entry->nesting > nesting)
+                       continue; /* Skip sub-table we didn't match */
+
+               if (entry->nesting < nesting)
+                       break; /* End of sub-table we were scanning */
+
+               if (!matched) {
+                       if ((insn & h->mask.bits) != h->value.bits)
+                               continue;
+                       entry->matched = true;
+               }
+
+               switch (type) {
+
+               case DECODE_TYPE_TABLE:
+                       ++nesting;
+                       break;
+
+               case DECODE_TYPE_CUSTOM:
+               case DECODE_TYPE_SIMULATE:
+               case DECODE_TYPE_EMULATE:
+                       coverage_add_registers(entry, insn);
+                       return;
+
+               case DECODE_TYPE_OR:
+                       matched = true;
+                       break;
+
+               case DECODE_TYPE_REJECT:
+               default:
+                       return;
+               }
+
+       }
+}
+
+static void coverage_end(void)
+{
+       struct coverage_entry *entry = coverage.base;
+       struct coverage_entry *end = coverage.base + coverage.num_entries;
+
+       for (; entry < end; ++entry) {
+               u32 mask = entry->header->mask.bits;
+               u32 value = entry->header->value.bits;
+<