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