config ARCH_SUPPORTS_AOUT
        def_bool y
 
+config IO_TRAPPED
+       bool
+
 source "init/Kconfig"
 
 menu "System type"
 
 obj-$(CONFIG_PM)               += pm.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_BINFMT_ELF)       += dump_task.o
+obj-$(CONFIG_IO_TRAPPED)       += io_trapped.o
 
 EXTRA_CFLAGS += -Werror
 
 obj-$(CONFIG_PM)               += pm.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_BINFMT_ELF)       += dump_task.o
+obj-$(CONFIG_IO_TRAPPED)       += io_trapped.o
 
 EXTRA_CFLAGS += -Werror
 
 
 void __iomem *ioport_map(unsigned long port, unsigned int nr)
 {
-       return sh_mv.mv_ioport_map(port, nr);
+       void __iomem *ret;
+
+       ret = __ioport_map_trapped(port, nr);
+       if (ret)
+               return ret;
+
+       return __ioport_map(port, nr);
 }
 EXPORT_SYMBOL(ioport_map);
 
 
 
 u8 generic_inb(unsigned long port)
 {
-       return ctrl_inb((unsigned long __force)ioport_map(port, 1));
+       return ctrl_inb((unsigned long __force)__ioport_map(port, 1));
 }
 
 u16 generic_inw(unsigned long port)
 {
-       return ctrl_inw((unsigned long __force)ioport_map(port, 2));
+       return ctrl_inw((unsigned long __force)__ioport_map(port, 2));
 }
 
 u32 generic_inl(unsigned long port)
 {
-       return ctrl_inl((unsigned long __force)ioport_map(port, 4));
+       return ctrl_inl((unsigned long __force)__ioport_map(port, 4));
 }
 
 u8 generic_inb_p(unsigned long port)
        volatile u8 *port_addr;
        u8 *buf = dst;
 
-       port_addr = (volatile u8 *)ioport_map(port, 1);
+       port_addr = (volatile u8 *)__ioport_map(port, 1);
        while (count--)
                *buf++ = *port_addr;
 }
        volatile u16 *port_addr;
        u16 *buf = dst;
 
-       port_addr = (volatile u16 *)ioport_map(port, 2);
+       port_addr = (volatile u16 *)__ioport_map(port, 2);
        while (count--)
                *buf++ = *port_addr;
 
        volatile u32 *port_addr;
        u32 *buf = dst;
 
-       port_addr = (volatile u32 *)ioport_map(port, 4);
+       port_addr = (volatile u32 *)__ioport_map(port, 4);
        while (count--)
                *buf++ = *port_addr;
 
 
 void generic_outb(u8 b, unsigned long port)
 {
-       ctrl_outb(b, (unsigned long __force)ioport_map(port, 1));
+       ctrl_outb(b, (unsigned long __force)__ioport_map(port, 1));
 }
 
 void generic_outw(u16 b, unsigned long port)
 {
-       ctrl_outw(b, (unsigned long __force)ioport_map(port, 2));
+       ctrl_outw(b, (unsigned long __force)__ioport_map(port, 2));
 }
 
 void generic_outl(u32 b, unsigned long port)
 {
-       ctrl_outl(b, (unsigned long __force)ioport_map(port, 4));
+       ctrl_outl(b, (unsigned long __force)__ioport_map(port, 4));
 }
 
 void generic_outb_p(u8 b, unsigned long port)
        volatile u8 *port_addr;
        const u8 *buf = src;
 
-       port_addr = (volatile u8 __force *)ioport_map(port, 1);
+       port_addr = (volatile u8 __force *)__ioport_map(port, 1);
 
        while (count--)
                *port_addr = *buf++;
        volatile u16 *port_addr;
        const u16 *buf = src;
 
-       port_addr = (volatile u16 __force *)ioport_map(port, 2);
+       port_addr = (volatile u16 __force *)__ioport_map(port, 2);
 
        while (count--)
                *port_addr = *buf++;
        volatile u32 *port_addr;
        const u32 *buf = src;
 
-       port_addr = (volatile u32 __force *)ioport_map(port, 4);
+       port_addr = (volatile u32 __force *)__ioport_map(port, 4);
        while (count--)
                *port_addr = *buf++;
 
 
--- /dev/null
+/*
+ * Trapped io support
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Intercept io operations by trapping.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/bitops.h>
+#include <linux/vmalloc.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/io_trapped.h>
+
+#define TRAPPED_PAGES_MAX 16
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+
+#ifdef CONFIG_HAS_IOPORT
+LIST_HEAD(trapped_io);
+#endif
+#ifdef CONFIG_HAS_IOMEM
+LIST_HEAD(trapped_mem);
+#endif
+static DEFINE_SPINLOCK(trapped_lock);
+
+int __init register_trapped_io(struct trapped_io *tiop)
+{
+       struct resource *res;
+       unsigned long len = 0, flags = 0;
+       struct page *pages[TRAPPED_PAGES_MAX];
+       int k, n;
+
+       /* structure must be page aligned */
+       if ((unsigned long)tiop & (PAGE_SIZE - 1))
+               goto bad;
+
+       for (k = 0; k < tiop->num_resources; k++) {
+               res = tiop->resource + k;
+               len += roundup((res->end - res->start) + 1, PAGE_SIZE);
+               flags |= res->flags;
+       }
+
+       /* support IORESOURCE_IO _or_ MEM, not both */
+       if (hweight_long(flags) != 1)
+               goto bad;
+
+       n = len >> PAGE_SHIFT;
+
+       if (n >= TRAPPED_PAGES_MAX)
+               goto bad;
+
+       for (k = 0; k < n; k++)
+               pages[k] = virt_to_page(tiop);
+
+       tiop->virt_base = vmap(pages, n, VM_MAP, PAGE_NONE);
+       if (!tiop->virt_base)
+               goto bad;
+
+       len = 0;
+       for (k = 0; k < tiop->num_resources; k++) {
+               res = tiop->resource + k;
+               pr_info("trapped io 0x%08lx overrides %s 0x%08lx\n",
+                      (unsigned long)(tiop->virt_base + len),
+                      res->flags & IORESOURCE_IO ? "io" : "mmio",
+                      (unsigned long)res->start);
+               len += roundup((res->end - res->start) + 1, PAGE_SIZE);
+       }
+
+       tiop->magic = IO_TRAPPED_MAGIC;
+       INIT_LIST_HEAD(&tiop->list);
+       spin_lock_irq(&trapped_lock);
+       if (flags & IORESOURCE_IO)
+               list_add(&tiop->list, &trapped_io);
+       if (flags & IORESOURCE_MEM)
+               list_add(&tiop->list, &trapped_mem);
+       spin_unlock_irq(&trapped_lock);
+
+       return 0;
+ bad:
+       pr_warning("unable to install trapped io filter\n");
+       return -1;
+}
+
+void __iomem *match_trapped_io_handler(struct list_head *list,
+                                      unsigned long offset,
+                                      unsigned long size)
+{
+       unsigned long voffs;
+       struct trapped_io *tiop;
+       struct resource *res;
+       int k, len;
+
+       spin_lock_irq(&trapped_lock);
+       list_for_each_entry(tiop, list, list) {
+               voffs = 0;
+               for (k = 0; k < tiop->num_resources; k++) {
+                       res = tiop->resource + k;
+                       if (res->start == offset) {
+                               spin_unlock_irq(&trapped_lock);
+                               return tiop->virt_base + voffs;
+                       }
+
+                       len = (res->end - res->start) + 1;
+                       voffs += roundup(len, PAGE_SIZE);
+               }
+       }
+       spin_unlock_irq(&trapped_lock);
+       return NULL;
+}
+
+static struct trapped_io *lookup_tiop(unsigned long address)
+{
+       pgd_t *pgd_k;
+       pud_t *pud_k;
+       pmd_t *pmd_k;
+       pte_t *pte_k;
+       pte_t entry;
+
+       pgd_k = swapper_pg_dir + pgd_index(address);
+       if (!pgd_present(*pgd_k))
+               return NULL;
+
+       pud_k = pud_offset(pgd_k, address);
+       if (!pud_present(*pud_k))
+               return NULL;
+
+       pmd_k = pmd_offset(pud_k, address);
+       if (!pmd_present(*pmd_k))
+               return NULL;
+
+       pte_k = pte_offset_kernel(pmd_k, address);
+       entry = *pte_k;
+
+       return pfn_to_kaddr(pte_pfn(entry));
+}
+
+static unsigned long lookup_address(struct trapped_io *tiop,
+                                   unsigned long address)
+{
+       struct resource *res;
+       unsigned long vaddr = (unsigned long)tiop->virt_base;
+       unsigned long len;
+       int k;
+
+       for (k = 0; k < tiop->num_resources; k++) {
+               res = tiop->resource + k;
+               len = roundup((res->end - res->start) + 1, PAGE_SIZE);
+               if (address < (vaddr + len))
+                       return res->start + (address - vaddr);
+               vaddr += len;
+       }
+       return 0;
+}
+
+static unsigned long long copy_word(unsigned long src_addr, int src_len,
+                                   unsigned long dst_addr, int dst_len)
+{
+       unsigned long long tmp = 0;
+
+       switch (src_len) {
+       case 1:
+               tmp = ctrl_inb(src_addr);
+               break;
+       case 2:
+               tmp = ctrl_inw(src_addr);
+               break;
+       case 4:
+               tmp = ctrl_inl(src_addr);
+               break;
+       case 8:
+               tmp = ctrl_inq(src_addr);
+               break;
+       }
+
+       switch (dst_len) {
+       case 1:
+               ctrl_outb(tmp, dst_addr);
+               break;
+       case 2:
+               ctrl_outw(tmp, dst_addr);
+               break;
+       case 4:
+               ctrl_outl(tmp, dst_addr);
+               break;
+       case 8:
+               ctrl_outq(tmp, dst_addr);
+               break;
+       }
+
+       return tmp;
+}
+
+static unsigned long from_device(void *dst, const void *src, unsigned long cnt)
+{
+       struct trapped_io *tiop;
+       unsigned long src_addr = (unsigned long)src;
+       unsigned long long tmp;
+
+       pr_debug("trapped io read 0x%08lx (%ld)\n", src_addr, cnt);
+       tiop = lookup_tiop(src_addr);
+       WARN_ON(!tiop || (tiop->magic != IO_TRAPPED_MAGIC));
+
+       src_addr = lookup_address(tiop, src_addr);
+       if (!src_addr)
+               return cnt;
+
+       tmp = copy_word(src_addr, MAX(cnt, (tiop->minimum_bus_width / 8)),
+                       (unsigned long)dst, cnt);
+
+       pr_debug("trapped io read 0x%08lx -> 0x%08llx\n", src_addr, tmp);
+       return 0;
+}
+
+static unsigned long to_device(void *dst, const void *src, unsigned long cnt)
+{
+       struct trapped_io *tiop;
+       unsigned long dst_addr = (unsigned long)dst;
+       unsigned long long tmp;
+
+       pr_debug("trapped io write 0x%08lx (%ld)\n", dst_addr, cnt);
+       tiop = lookup_tiop(dst_addr);
+       WARN_ON(!tiop || (tiop->magic != IO_TRAPPED_MAGIC));
+
+       dst_addr = lookup_address(tiop, dst_addr);
+       if (!dst_addr)
+               return cnt;
+
+       tmp = copy_word((unsigned long)src, cnt,
+                       dst_addr, MAX(cnt, (tiop->minimum_bus_width / 8)));
+
+       pr_debug("trapped io write 0x%08lx -> 0x%08llx\n", dst_addr, tmp);
+       return 0;
+}
+
+static struct mem_access trapped_io_access = {
+       from_device,
+       to_device,
+};
+
+int handle_trapped_io(struct pt_regs *regs, unsigned long address)
+{
+       mm_segment_t oldfs;
+       opcode_t instruction;
+       int tmp;
+
+       if (!lookup_tiop(address))
+               return 0;
+
+       WARN_ON(user_mode(regs));
+
+       oldfs = get_fs();
+       set_fs(KERNEL_DS);
+       if (copy_from_user(&instruction, (void *)(regs->pc),
+                          sizeof(instruction))) {
+               set_fs(oldfs);
+               return 0;
+       }
+
+       tmp = handle_unaligned_access(instruction, regs, &trapped_io_access);
+       set_fs(oldfs);
+       return tmp == 0;
+}
 
 #endif
 }
 
+static struct mem_access user_mem_access = {
+       copy_from_user,
+       copy_to_user,
+};
+
 /*
  * handle an instruction that does an unaligned memory access by emulating the
  * desired behaviour
  *   (if that instruction is in a branch delay slot)
  * - return 0 if emulation okay, -EFAULT on existential error
  */
-static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs)
+static int handle_unaligned_ins(opcode_t instruction, struct pt_regs *regs,
+                               struct mem_access *ma)
 {
        int ret, index, count;
        unsigned long *rm, *rn;
 #if !defined(__LITTLE_ENDIAN__)
                        dst += 4-count;
 #endif
-                       if (copy_from_user(dst, src, count))
+                       if (ma->from(dst, src, count))
                                goto fetch_fault;
 
                        sign_extend(count, dst);
                        dst = (unsigned char*) *rn;
                        dst += regs->regs[0];
 
-                       if (copy_to_user(dst, src, count))
+                       if (ma->to(dst, src, count))
                                goto fetch_fault;
                }
                ret = 0;
                dst = (unsigned char*) *rn;
                dst += (instruction&0x000F)<<2;
 
-               if (copy_to_user(dst,src,4))
+               if (ma->to(dst, src, 4))
                        goto fetch_fault;
                ret = 0;
                break;
 #if !defined(__LITTLE_ENDIAN__)
                src += 4-count;
 #endif
-               if (copy_to_user(dst, src, count))
+               if (ma->to(dst, src, count))
                        goto fetch_fault;
                ret = 0;
                break;
                dst = (unsigned char*) rn;
                *(unsigned long*)dst = 0;
 
-               if (copy_from_user(dst,src,4))
+               if (ma->from(dst, src, 4))
                        goto fetch_fault;
                ret = 0;
                break;
 #if !defined(__LITTLE_ENDIAN__)
                dst += 4-count;
 #endif
-               if (copy_from_user(dst, src, count))
+               if (ma->from(dst, src, count))
                        goto fetch_fault;
                sign_extend(count, dst);
                ret = 0;
                        dst = (unsigned char*) *rm; /* called Rn in the spec */
                        dst += (instruction&0x000F)<<1;
 
-                       if (copy_to_user(dst, src, 2))
+                       if (ma->to(dst, src, 2))
                                goto fetch_fault;
                        ret = 0;
                        break;
 #if !defined(__LITTLE_ENDIAN__)
                        dst += 2;
 #endif
-                       if (copy_from_user(dst, src, 2))
+                       if (ma->from(dst, src, 2))
                                goto fetch_fault;
                        sign_extend(2, dst);
                        ret = 0;
  * emulate the instruction in the delay slot
  * - fetches the instruction from PC+2
  */
-static inline int handle_unaligned_delayslot(struct pt_regs *regs,
-                                            opcode_t old_instruction)
+static inline int handle_delayslot(struct pt_regs *regs,
+                                  opcode_t old_instruction,
+                                  struct mem_access *ma)
 {
        opcode_t instruction;
        void *addr = (void *)(regs->pc + instruction_size(old_instruction));
                    regs, 0);
        }
 
-       return handle_unaligned_ins(instruction, regs);
+       return handle_unaligned_ins(instruction, regs, ma);
 }
 
 /*
 
 static int handle_unaligned_notify_count = 10;
 
-static int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs)
+int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs,
+                           struct mem_access *ma)
 {
        u_int rm;
        int ret, index;
        case 0x0000:
                if (instruction==0x000B) {
                        /* rts */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0)
                                regs->pc = regs->pr;
                }
                else if ((instruction&0x00FF)==0x0023) {
                        /* braf @Rm */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0)
                                regs->pc += rm + 4;
                }
                else if ((instruction&0x00FF)==0x0003) {
                        /* bsrf @Rm */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
                                regs->pr = regs->pc + 4;
                                regs->pc += rm + 4;
        case 0x4000:
                if ((instruction&0x00FF)==0x002B) {
                        /* jmp @Rm */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0)
                                regs->pc = rm;
                }
                else if ((instruction&0x00FF)==0x000B) {
                        /* jsr @Rm */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
                                regs->pr = regs->pc + 4;
                                regs->pc = rm;
                case 0x0B00: /* bf   lab - no delayslot*/
                        break;
                case 0x0F00: /* bf/s lab */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
                                if ((regs->sr & 0x00000001) != 0)
                case 0x0900: /* bt   lab - no delayslot */
                        break;
                case 0x0D00: /* bt/s lab */
-                       ret = handle_unaligned_delayslot(regs, instruction);
+                       ret = handle_delayslot(regs, instruction, ma);
                        if (ret==0) {
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
                                if ((regs->sr & 0x00000001) == 0)
                break;
 
        case 0xA000: /* bra label */
-               ret = handle_unaligned_delayslot(regs, instruction);
+               ret = handle_delayslot(regs, instruction, ma);
                if (ret==0)
                        regs->pc += SH_PC_12BIT_OFFSET(instruction);
                break;
 
        case 0xB000: /* bsr label */
-               ret = handle_unaligned_delayslot(regs, instruction);
+               ret = handle_delayslot(regs, instruction, ma);
                if (ret==0) {
                        regs->pr = regs->pc + 4;
                        regs->pc += SH_PC_12BIT_OFFSET(instruction);
 
        /* handle non-delay-slot instruction */
  simple:
-       ret = handle_unaligned_ins(instruction, regs);
+       ret = handle_unaligned_ins(instruction, regs, ma);
        if (ret==0)
                regs->pc += instruction_size(instruction);
        return ret;
                        goto uspace_segv;
                }
 
-               tmp = handle_unaligned_access(instruction, regs);
+               tmp = handle_unaligned_access(instruction, regs,
+                                             &user_mem_access);
                set_fs(oldfs);
 
                if (tmp==0)
                        die("insn faulting in do_address_error", regs, 0);
                }
 
-               handle_unaligned_access(instruction, regs);
+               handle_unaligned_access(instruction, regs, &user_mem_access);
                set_fs(oldfs);
        }
 }
 
 #include <linux/mm.h>
 #include <linux/hardirq.h>
 #include <linux/kprobes.h>
+#include <asm/io_trapped.h>
 #include <asm/system.h>
 #include <asm/mmu_context.h>
 #include <asm/tlbflush.h>
        if (fixup_exception(regs))
                return;
 
+       if (handle_trapped_io(regs, address))
+               return;
 /*
  * Oops. The kernel tried to access some bad page. We'll have to
  * terminate things with extreme prejudice.
 
  */
 #define __IO_PREFIX    generic
 #include <asm/io_generic.h>
+#include <asm/io_trapped.h>
 
 #define maybebadio(port) \
   printk(KERN_ERR "bad PC-like io %s:%u for port 0x%lx at 0x%08x\n", \
        generic_io_base = pbase;
 }
 
+#define __ioport_map(p, n) sh_mv.mv_ioport_map((p), (n))
+
 /* We really want to try and get these to memcpy etc */
 extern void memcpy_fromio(void *, volatile void __iomem *, unsigned long);
 extern void memcpy_toio(volatile void __iomem *, const void *, unsigned long);
 {
 #ifdef CONFIG_SUPERH32
        unsigned long last_addr = offset + size - 1;
+#endif
+       void __iomem *ret;
 
+       ret = __ioremap_trapped(offset, size);
+       if (ret)
+               return ret;
+
+#ifdef CONFIG_SUPERH32
        /*
         * For P1 and P2 space this is trivial, as everything is already
         * mapped. Uncached access for P1 addresses are done through P2.
 
--- /dev/null
+#ifndef __ASM_SH_IO_TRAPPED_H
+#define __ASM_SH_IO_TRAPPED_H
+
+#include <linux/list.h>
+#include <linux/ioport.h>
+#include <asm/page.h>
+
+#define IO_TRAPPED_MAGIC 0xfeedbeef
+
+struct trapped_io {
+       unsigned int magic;
+       struct resource *resource;
+       unsigned int num_resources;
+       unsigned int minimum_bus_width;
+       struct list_head list;
+       void __iomem *virt_base;
+} __aligned(PAGE_SIZE);
+
+#ifdef CONFIG_IO_TRAPPED
+int register_trapped_io(struct trapped_io *tiop);
+int handle_trapped_io(struct pt_regs *regs, unsigned long address);
+
+void __iomem *match_trapped_io_handler(struct list_head *list,
+                                      unsigned long offset,
+                                      unsigned long size);
+
+#ifdef CONFIG_HAS_IOMEM
+extern struct list_head trapped_mem;
+
+static inline void __iomem *
+__ioremap_trapped(unsigned long offset, unsigned long size)
+{
+       return match_trapped_io_handler(&trapped_mem, offset, size);
+}
+#else
+#define __ioremap_trapped(offset, size) NULL
+#endif
+
+#ifdef CONFIG_HAS_IOPORT
+extern struct list_head trapped_io;
+
+static inline void __iomem *
+__ioport_map_trapped(unsigned long offset, unsigned long size)
+{
+       return match_trapped_io_handler(&trapped_io, offset, size);
+}
+#else
+#define __ioport_map_trapped(offset, size) NULL
+#endif
+
+#else
+#define register_trapped_io(tiop) (-1)
+#define handle_trapped_io(tiop, address) 0
+#define __ioremap_trapped(offset, size) NULL
+#define __ioport_map_trapped(offset, size) NULL
+#endif
+
+#endif /* __ASM_SH_IO_TRAPPED_H */
 
 
 #define arch_align_stack(x) (x)
 
+struct mem_access {
+       unsigned long (*from)(void *dst, const void *src, unsigned long cnt);
+       unsigned long (*to)(void *dst, const void *src, unsigned long cnt);
+};
+
 #ifdef CONFIG_SUPERH32
 # include "system_32.h"
 #else
 
                : "=&r" (__dummy));                     \
 } while (0)
 
+int handle_unaligned_access(opcode_t instruction, struct pt_regs *regs,
+                           struct mem_access *ma);
+
 #endif /* __ASM_SH_SYSTEM_32_H */