1e7cc9a73SMagnus Damm /* 2e7cc9a73SMagnus Damm * Trapped io support 3e7cc9a73SMagnus Damm * 4e7cc9a73SMagnus Damm * Copyright (C) 2008 Magnus Damm 5e7cc9a73SMagnus Damm * 6e7cc9a73SMagnus Damm * Intercept io operations by trapping. 7e7cc9a73SMagnus Damm * 8e7cc9a73SMagnus Damm * This file is subject to the terms and conditions of the GNU General Public 9e7cc9a73SMagnus Damm * License. See the file "COPYING" in the main directory of this archive 10e7cc9a73SMagnus Damm * for more details. 11e7cc9a73SMagnus Damm */ 12e7cc9a73SMagnus Damm #include <linux/kernel.h> 13e7cc9a73SMagnus Damm #include <linux/mm.h> 14e7cc9a73SMagnus Damm #include <linux/bitops.h> 15e7cc9a73SMagnus Damm #include <linux/vmalloc.h> 16*ecc14e8cSPaul Mundt #include <linux/module.h> 17e7cc9a73SMagnus Damm #include <asm/system.h> 18e7cc9a73SMagnus Damm #include <asm/mmu_context.h> 19e7cc9a73SMagnus Damm #include <asm/uaccess.h> 20e7cc9a73SMagnus Damm #include <asm/io.h> 21e7cc9a73SMagnus Damm #include <asm/io_trapped.h> 22e7cc9a73SMagnus Damm 23e7cc9a73SMagnus Damm #define TRAPPED_PAGES_MAX 16 24e7cc9a73SMagnus Damm 25e7cc9a73SMagnus Damm #ifdef CONFIG_HAS_IOPORT 26e7cc9a73SMagnus Damm LIST_HEAD(trapped_io); 27*ecc14e8cSPaul Mundt EXPORT_SYMBOL_GPL(trapped_io); 28e7cc9a73SMagnus Damm #endif 29e7cc9a73SMagnus Damm #ifdef CONFIG_HAS_IOMEM 30e7cc9a73SMagnus Damm LIST_HEAD(trapped_mem); 31*ecc14e8cSPaul Mundt EXPORT_SYMBOL_GPL(trapped_mem); 32e7cc9a73SMagnus Damm #endif 33e7cc9a73SMagnus Damm static DEFINE_SPINLOCK(trapped_lock); 34e7cc9a73SMagnus Damm 35e7cc9a73SMagnus Damm int __init register_trapped_io(struct trapped_io *tiop) 36e7cc9a73SMagnus Damm { 37e7cc9a73SMagnus Damm struct resource *res; 38e7cc9a73SMagnus Damm unsigned long len = 0, flags = 0; 39e7cc9a73SMagnus Damm struct page *pages[TRAPPED_PAGES_MAX]; 40e7cc9a73SMagnus Damm int k, n; 41e7cc9a73SMagnus Damm 42e7cc9a73SMagnus Damm /* structure must be page aligned */ 43e7cc9a73SMagnus Damm if ((unsigned long)tiop & (PAGE_SIZE - 1)) 44e7cc9a73SMagnus Damm goto bad; 45e7cc9a73SMagnus Damm 46e7cc9a73SMagnus Damm for (k = 0; k < tiop->num_resources; k++) { 47e7cc9a73SMagnus Damm res = tiop->resource + k; 48e7cc9a73SMagnus Damm len += roundup((res->end - res->start) + 1, PAGE_SIZE); 49e7cc9a73SMagnus Damm flags |= res->flags; 50e7cc9a73SMagnus Damm } 51e7cc9a73SMagnus Damm 52e7cc9a73SMagnus Damm /* support IORESOURCE_IO _or_ MEM, not both */ 53e7cc9a73SMagnus Damm if (hweight_long(flags) != 1) 54e7cc9a73SMagnus Damm goto bad; 55e7cc9a73SMagnus Damm 56e7cc9a73SMagnus Damm n = len >> PAGE_SHIFT; 57e7cc9a73SMagnus Damm 58e7cc9a73SMagnus Damm if (n >= TRAPPED_PAGES_MAX) 59e7cc9a73SMagnus Damm goto bad; 60e7cc9a73SMagnus Damm 61e7cc9a73SMagnus Damm for (k = 0; k < n; k++) 62e7cc9a73SMagnus Damm pages[k] = virt_to_page(tiop); 63e7cc9a73SMagnus Damm 64e7cc9a73SMagnus Damm tiop->virt_base = vmap(pages, n, VM_MAP, PAGE_NONE); 65e7cc9a73SMagnus Damm if (!tiop->virt_base) 66e7cc9a73SMagnus Damm goto bad; 67e7cc9a73SMagnus Damm 68e7cc9a73SMagnus Damm len = 0; 69e7cc9a73SMagnus Damm for (k = 0; k < tiop->num_resources; k++) { 70e7cc9a73SMagnus Damm res = tiop->resource + k; 71e7cc9a73SMagnus Damm pr_info("trapped io 0x%08lx overrides %s 0x%08lx\n", 72e7cc9a73SMagnus Damm (unsigned long)(tiop->virt_base + len), 73e7cc9a73SMagnus Damm res->flags & IORESOURCE_IO ? "io" : "mmio", 74e7cc9a73SMagnus Damm (unsigned long)res->start); 75e7cc9a73SMagnus Damm len += roundup((res->end - res->start) + 1, PAGE_SIZE); 76e7cc9a73SMagnus Damm } 77e7cc9a73SMagnus Damm 78e7cc9a73SMagnus Damm tiop->magic = IO_TRAPPED_MAGIC; 79e7cc9a73SMagnus Damm INIT_LIST_HEAD(&tiop->list); 80e7cc9a73SMagnus Damm spin_lock_irq(&trapped_lock); 81e7cc9a73SMagnus Damm if (flags & IORESOURCE_IO) 82e7cc9a73SMagnus Damm list_add(&tiop->list, &trapped_io); 83e7cc9a73SMagnus Damm if (flags & IORESOURCE_MEM) 84e7cc9a73SMagnus Damm list_add(&tiop->list, &trapped_mem); 85e7cc9a73SMagnus Damm spin_unlock_irq(&trapped_lock); 86e7cc9a73SMagnus Damm 87e7cc9a73SMagnus Damm return 0; 88e7cc9a73SMagnus Damm bad: 89e7cc9a73SMagnus Damm pr_warning("unable to install trapped io filter\n"); 90e7cc9a73SMagnus Damm return -1; 91e7cc9a73SMagnus Damm } 92*ecc14e8cSPaul Mundt EXPORT_SYMBOL_GPL(register_trapped_io); 93e7cc9a73SMagnus Damm 94e7cc9a73SMagnus Damm void __iomem *match_trapped_io_handler(struct list_head *list, 95e7cc9a73SMagnus Damm unsigned long offset, 96e7cc9a73SMagnus Damm unsigned long size) 97e7cc9a73SMagnus Damm { 98e7cc9a73SMagnus Damm unsigned long voffs; 99e7cc9a73SMagnus Damm struct trapped_io *tiop; 100e7cc9a73SMagnus Damm struct resource *res; 101e7cc9a73SMagnus Damm int k, len; 102e7cc9a73SMagnus Damm 103e7cc9a73SMagnus Damm spin_lock_irq(&trapped_lock); 104e7cc9a73SMagnus Damm list_for_each_entry(tiop, list, list) { 105e7cc9a73SMagnus Damm voffs = 0; 106e7cc9a73SMagnus Damm for (k = 0; k < tiop->num_resources; k++) { 107e7cc9a73SMagnus Damm res = tiop->resource + k; 108e7cc9a73SMagnus Damm if (res->start == offset) { 109e7cc9a73SMagnus Damm spin_unlock_irq(&trapped_lock); 110e7cc9a73SMagnus Damm return tiop->virt_base + voffs; 111e7cc9a73SMagnus Damm } 112e7cc9a73SMagnus Damm 113e7cc9a73SMagnus Damm len = (res->end - res->start) + 1; 114e7cc9a73SMagnus Damm voffs += roundup(len, PAGE_SIZE); 115e7cc9a73SMagnus Damm } 116e7cc9a73SMagnus Damm } 117e7cc9a73SMagnus Damm spin_unlock_irq(&trapped_lock); 118e7cc9a73SMagnus Damm return NULL; 119e7cc9a73SMagnus Damm } 120*ecc14e8cSPaul Mundt EXPORT_SYMBOL_GPL(match_trapped_io_handler); 121e7cc9a73SMagnus Damm 122e7cc9a73SMagnus Damm static struct trapped_io *lookup_tiop(unsigned long address) 123e7cc9a73SMagnus Damm { 124e7cc9a73SMagnus Damm pgd_t *pgd_k; 125e7cc9a73SMagnus Damm pud_t *pud_k; 126e7cc9a73SMagnus Damm pmd_t *pmd_k; 127e7cc9a73SMagnus Damm pte_t *pte_k; 128e7cc9a73SMagnus Damm pte_t entry; 129e7cc9a73SMagnus Damm 130e7cc9a73SMagnus Damm pgd_k = swapper_pg_dir + pgd_index(address); 131e7cc9a73SMagnus Damm if (!pgd_present(*pgd_k)) 132e7cc9a73SMagnus Damm return NULL; 133e7cc9a73SMagnus Damm 134e7cc9a73SMagnus Damm pud_k = pud_offset(pgd_k, address); 135e7cc9a73SMagnus Damm if (!pud_present(*pud_k)) 136e7cc9a73SMagnus Damm return NULL; 137e7cc9a73SMagnus Damm 138e7cc9a73SMagnus Damm pmd_k = pmd_offset(pud_k, address); 139e7cc9a73SMagnus Damm if (!pmd_present(*pmd_k)) 140e7cc9a73SMagnus Damm return NULL; 141e7cc9a73SMagnus Damm 142e7cc9a73SMagnus Damm pte_k = pte_offset_kernel(pmd_k, address); 143e7cc9a73SMagnus Damm entry = *pte_k; 144e7cc9a73SMagnus Damm 145e7cc9a73SMagnus Damm return pfn_to_kaddr(pte_pfn(entry)); 146e7cc9a73SMagnus Damm } 147e7cc9a73SMagnus Damm 148e7cc9a73SMagnus Damm static unsigned long lookup_address(struct trapped_io *tiop, 149e7cc9a73SMagnus Damm unsigned long address) 150e7cc9a73SMagnus Damm { 151e7cc9a73SMagnus Damm struct resource *res; 152e7cc9a73SMagnus Damm unsigned long vaddr = (unsigned long)tiop->virt_base; 153e7cc9a73SMagnus Damm unsigned long len; 154e7cc9a73SMagnus Damm int k; 155e7cc9a73SMagnus Damm 156e7cc9a73SMagnus Damm for (k = 0; k < tiop->num_resources; k++) { 157e7cc9a73SMagnus Damm res = tiop->resource + k; 158e7cc9a73SMagnus Damm len = roundup((res->end - res->start) + 1, PAGE_SIZE); 159e7cc9a73SMagnus Damm if (address < (vaddr + len)) 160e7cc9a73SMagnus Damm return res->start + (address - vaddr); 161e7cc9a73SMagnus Damm vaddr += len; 162e7cc9a73SMagnus Damm } 163e7cc9a73SMagnus Damm return 0; 164e7cc9a73SMagnus Damm } 165e7cc9a73SMagnus Damm 166e7cc9a73SMagnus Damm static unsigned long long copy_word(unsigned long src_addr, int src_len, 167e7cc9a73SMagnus Damm unsigned long dst_addr, int dst_len) 168e7cc9a73SMagnus Damm { 169e7cc9a73SMagnus Damm unsigned long long tmp = 0; 170e7cc9a73SMagnus Damm 171e7cc9a73SMagnus Damm switch (src_len) { 172e7cc9a73SMagnus Damm case 1: 173e7cc9a73SMagnus Damm tmp = ctrl_inb(src_addr); 174e7cc9a73SMagnus Damm break; 175e7cc9a73SMagnus Damm case 2: 176e7cc9a73SMagnus Damm tmp = ctrl_inw(src_addr); 177e7cc9a73SMagnus Damm break; 178e7cc9a73SMagnus Damm case 4: 179e7cc9a73SMagnus Damm tmp = ctrl_inl(src_addr); 180e7cc9a73SMagnus Damm break; 181e7cc9a73SMagnus Damm case 8: 182e7cc9a73SMagnus Damm tmp = ctrl_inq(src_addr); 183e7cc9a73SMagnus Damm break; 184e7cc9a73SMagnus Damm } 185e7cc9a73SMagnus Damm 186e7cc9a73SMagnus Damm switch (dst_len) { 187e7cc9a73SMagnus Damm case 1: 188e7cc9a73SMagnus Damm ctrl_outb(tmp, dst_addr); 189e7cc9a73SMagnus Damm break; 190e7cc9a73SMagnus Damm case 2: 191e7cc9a73SMagnus Damm ctrl_outw(tmp, dst_addr); 192e7cc9a73SMagnus Damm break; 193e7cc9a73SMagnus Damm case 4: 194e7cc9a73SMagnus Damm ctrl_outl(tmp, dst_addr); 195e7cc9a73SMagnus Damm break; 196e7cc9a73SMagnus Damm case 8: 197e7cc9a73SMagnus Damm ctrl_outq(tmp, dst_addr); 198e7cc9a73SMagnus Damm break; 199e7cc9a73SMagnus Damm } 200e7cc9a73SMagnus Damm 201e7cc9a73SMagnus Damm return tmp; 202e7cc9a73SMagnus Damm } 203e7cc9a73SMagnus Damm 204e7cc9a73SMagnus Damm static unsigned long from_device(void *dst, const void *src, unsigned long cnt) 205e7cc9a73SMagnus Damm { 206e7cc9a73SMagnus Damm struct trapped_io *tiop; 207e7cc9a73SMagnus Damm unsigned long src_addr = (unsigned long)src; 208e7cc9a73SMagnus Damm unsigned long long tmp; 209e7cc9a73SMagnus Damm 210e7cc9a73SMagnus Damm pr_debug("trapped io read 0x%08lx (%ld)\n", src_addr, cnt); 211e7cc9a73SMagnus Damm tiop = lookup_tiop(src_addr); 212e7cc9a73SMagnus Damm WARN_ON(!tiop || (tiop->magic != IO_TRAPPED_MAGIC)); 213e7cc9a73SMagnus Damm 214e7cc9a73SMagnus Damm src_addr = lookup_address(tiop, src_addr); 215e7cc9a73SMagnus Damm if (!src_addr) 216e7cc9a73SMagnus Damm return cnt; 217e7cc9a73SMagnus Damm 218f1cdd63fSPaul Mundt tmp = copy_word(src_addr, 219f1cdd63fSPaul Mundt max_t(unsigned long, cnt, 220f1cdd63fSPaul Mundt (tiop->minimum_bus_width / 8)), 221e7cc9a73SMagnus Damm (unsigned long)dst, cnt); 222e7cc9a73SMagnus Damm 223e7cc9a73SMagnus Damm pr_debug("trapped io read 0x%08lx -> 0x%08llx\n", src_addr, tmp); 224e7cc9a73SMagnus Damm return 0; 225e7cc9a73SMagnus Damm } 226e7cc9a73SMagnus Damm 227e7cc9a73SMagnus Damm static unsigned long to_device(void *dst, const void *src, unsigned long cnt) 228e7cc9a73SMagnus Damm { 229e7cc9a73SMagnus Damm struct trapped_io *tiop; 230e7cc9a73SMagnus Damm unsigned long dst_addr = (unsigned long)dst; 231e7cc9a73SMagnus Damm unsigned long long tmp; 232e7cc9a73SMagnus Damm 233e7cc9a73SMagnus Damm pr_debug("trapped io write 0x%08lx (%ld)\n", dst_addr, cnt); 234e7cc9a73SMagnus Damm tiop = lookup_tiop(dst_addr); 235e7cc9a73SMagnus Damm WARN_ON(!tiop || (tiop->magic != IO_TRAPPED_MAGIC)); 236e7cc9a73SMagnus Damm 237e7cc9a73SMagnus Damm dst_addr = lookup_address(tiop, dst_addr); 238e7cc9a73SMagnus Damm if (!dst_addr) 239e7cc9a73SMagnus Damm return cnt; 240e7cc9a73SMagnus Damm 241e7cc9a73SMagnus Damm tmp = copy_word((unsigned long)src, cnt, 242f1cdd63fSPaul Mundt dst_addr, max_t(unsigned long, cnt, 243f1cdd63fSPaul Mundt (tiop->minimum_bus_width / 8))); 244e7cc9a73SMagnus Damm 245e7cc9a73SMagnus Damm pr_debug("trapped io write 0x%08lx -> 0x%08llx\n", dst_addr, tmp); 246e7cc9a73SMagnus Damm return 0; 247e7cc9a73SMagnus Damm } 248e7cc9a73SMagnus Damm 249e7cc9a73SMagnus Damm static struct mem_access trapped_io_access = { 250e7cc9a73SMagnus Damm from_device, 251e7cc9a73SMagnus Damm to_device, 252e7cc9a73SMagnus Damm }; 253e7cc9a73SMagnus Damm 254e7cc9a73SMagnus Damm int handle_trapped_io(struct pt_regs *regs, unsigned long address) 255e7cc9a73SMagnus Damm { 256e7cc9a73SMagnus Damm mm_segment_t oldfs; 257e7cc9a73SMagnus Damm opcode_t instruction; 258e7cc9a73SMagnus Damm int tmp; 259e7cc9a73SMagnus Damm 260e7cc9a73SMagnus Damm if (!lookup_tiop(address)) 261e7cc9a73SMagnus Damm return 0; 262e7cc9a73SMagnus Damm 263e7cc9a73SMagnus Damm WARN_ON(user_mode(regs)); 264e7cc9a73SMagnus Damm 265e7cc9a73SMagnus Damm oldfs = get_fs(); 266e7cc9a73SMagnus Damm set_fs(KERNEL_DS); 267e7cc9a73SMagnus Damm if (copy_from_user(&instruction, (void *)(regs->pc), 268e7cc9a73SMagnus Damm sizeof(instruction))) { 269e7cc9a73SMagnus Damm set_fs(oldfs); 270e7cc9a73SMagnus Damm return 0; 271e7cc9a73SMagnus Damm } 272e7cc9a73SMagnus Damm 273e7cc9a73SMagnus Damm tmp = handle_unaligned_access(instruction, regs, &trapped_io_access); 274e7cc9a73SMagnus Damm set_fs(oldfs); 275e7cc9a73SMagnus Damm return tmp == 0; 276e7cc9a73SMagnus Damm } 277